半平面求交 - 洛谷 - UVA1475 Jungle Outpost

news2024/11/24 2:48:56

欢迎关注更多精彩
关注我,学习常用算法与数据结构,一题多解,降维打击。

往期相关背景点击前往

题目大意

题目链接
https://www.luogu.com.cn/problem/UVA1475

在丛林里有n个瞭望塔,瞭望塔的保护范围就是一个凸n多边形,敌人进攻会炸毁一些瞭望塔,使总部暴露在剩下的瞭望塔围成的凸多边形之外,请你选择一个点作为总部,使得敌人要炸毁的瞭望塔尽量多。

解析

此题提到最多要多少个,首先想到枚举+验证的方式。

枚举可以利用二分优化,前提是要证明答案是具体有单调性,既如果炸x个塔不能摧毁防御,那么炸x-1个塔也不能摧毁防御。如果炸x个塔可以摧毁防御,那么炸x+1个塔也可以摧毁防御。

可以先尝试炸1个的情形。
在这里插入图片描述

炸1个那么就是把剩下两个点连边。把所有点都炸1遍,可以看到最后有效防御区域是一个半平面求交凸多边形。上图中表示为黄色区域。

接下来看炸2个的情况。

炸两个有2种策略:

  1. 炸连续的两个塔
  2. 炸不连续的两个塔

先看炸不连续的情形,

在这里插入图片描述

如果炸不连续的两个塔,则会退化成炸1个塔情形,并不是最优解。 从这里可以推断出,这个是一单调的问题。

看炸连续的两个塔,

在这里插入图片描述
没有公共区域。

综上所述,如果连续炸掉x个塔,那么答案是关于x单调的。

可以通过二分枚举x, 再构建半平面求交验证的方法解决。

二分枚举点击前往

代码

#include<stdio.h>
#include<cmath>
#include <algorithm>
#include <vector>
#include <list>
#include <cstring>
#include <deque>


using namespace std;
const double EPS = 1e-7;

const int N = 1e5 + 10;

int cmp(double d) {
	if (abs(d) < EPS)return 0;
	if (d > 0)return 1;
	return -1;
}

class Point {
public:
	double x, y;
	int id;

	Point() {}
	Point(double a, double b) :x(a), y(b) {}
	Point(const Point& p) :x(p.x), y(p.y), id(p.id) {}

	void in() {
		scanf("%lf %lf", &x, &y);
	}
	void out() {
		printf("%f %f\n", x, y);
	}

	double dis() {
		return sqrt(x * x + y * y);
	}

	double dis2() {
		return x * x + y * y;
	}

	Point operator -() const {
		return Point(-x, -y);
	}

	Point operator -(const Point& p) const {
		return Point(x - p.x, y - p.y);
	}

	Point operator +(const Point& p) const {
		return Point(x + p.x, y + p.y);
	}
	Point operator *(double d)const {
		return Point(x * d, y * d);
	}

	Point operator /(double d)const {
		return Point(x / d, y / d);
	}


	void operator -=(Point& p) {
		x -= p.x;
		y -= p.y;
	}

	void operator +=(Point& p) {
		x += p.x;
		y += p.y;
	}
	void operator *=(double d) {
		x *= d;
		y *= d;
	}

	void operator /=(double d) {
		this ->operator*= (1 / d);
	}

	bool operator<(const Point& a) const {
		return x < a.x || (abs(x - a.x) < EPS && y < a.y);
	}

	bool operator==(const Point& a) const {
		return abs(x - a.x) < EPS && abs(y - a.y) < EPS;
	}
};

// 向量操作

double cross(const Point& a, const Point& b) {
	return a.x * b.y - a.y * b.x;
}

double dot(const Point& a, const Point& b) {
	return a.x * b.x + a.y * b.y;
}


class Line {
public:
	Point front, tail;
	double ang;
	Line() {}
	Line(const Point& a, const Point& b) :front(a), tail(b) {
		ang = atan2(front.y - tail.y, front.x - tail.x);
	}
};

int cmp(const Line& a, const Line& b) {
	//auto ta = atan2(a.front.y - a.tail.y, a.front.x - a.tail.x);
	//auto tb = atan2(b.front.y - b.tail.y, b.front.x - b.tail.x);

	return cmp(a.ang - b.ang);
}


// 点在直线哪一边>0 左边,<0边
int SideJudge(const Line& a, const Point& b) {
	return cmp(cross(a.front - a.tail, b - a.tail));
}


int LineSort(const Line& a, const Line& b) {
	int c = cmp(a, b);
	if (c)return c < 0;
	return SideJudge(b, a.front) > 0;
}

/*
点p 到 p+r 表示线段1
点q 到 q+s 表示线段2
线段1 上1点用 p' = p+t*r (0<=t<=1)
线段2 上1点用 q' = q+u*s (0<=u<=1)
让两式相等求交点 p+t*r = q+u*s
两边都叉乘s
(p+t*r)Xs = (q+u*s)Xs
pXs + t*rXs = qXs
t = (q-p)Xs/(rXs)
同理,
u = (p-q)Xr/(sXr) -> u = (q-p)Xr/(rXs)

以下分4种情况:
1. 共线,sXr==0 && (q-p)Xr==0, 计算 (q-p)在r上的投影在r长度上的占比t0,
计算(q+s-p)在r上的投影在r长度上的占比t1,查看[t0, t1]是否与范围[0,1]有交集。
如果t0>t1, 则比较[t1, t0]是否与范围[0,1]有交集。
t0 = (q-p)*r/(r*r)
t1 = (q+s-p)*r/(r*r) = t0 + s · r / (r · r)
2. 平行sXr==0 && (q-p)Xr!=0
3. 0<=u<=1 && 0<=t<=1 有交点
4. 其他u, t不在0到范围内,没有交点。
*/
pair<double, double> intersection(const Point& q, const Point& s, const Point& p, const Point& r) {
	// 计算 (q-p)Xr
	auto qpr = cross(q - p, r);
	auto qps = cross(q - p, s);

	auto rXs = cross(r, s);
	if (cmp(rXs) == 0)return { -1, -1 }; // 平行或共线
	// 求解t, u
	// t = (q-p)Xs/(rXs)
	auto t = qps / rXs;

	// u = (q-p)Xr/(rXs)
	auto u = qpr / rXs;

	return { u, t };
}

Point LineCross(const Line& a, const Line& b) {
	Point dira = a.front - a.tail;
	Point dirb = b.front - b.tail;

	auto p = intersection(a.tail, dira, b.tail, dirb);
	return a.tail + dira * p.first;
}

class HalfPlane {
public:
	vector<Line> lines;
	vector<int> q;
	vector<Point> t;
	int len;

	HalfPlane() {
		lines.resize(N);
		q.resize(N);
		t.resize(N);
	}

	void addLine(const Line& a) {
		lines[len++]=a;
	}

	vector<Point> run() {
		sort(lines.begin(), lines.begin()+len, LineSort);

		int l = -1, r = 0;
		q[0] = 0;
		for (int i = 1; i < len; ++i) {
			if (cmp(lines[i], lines[i - 1]) == 0)continue;
			while (r - l > 1 && SideJudge(lines[i], t[r]) < 0)r--;
			while (r - l > 1 && SideJudge(lines[i], t[l + 2]) < 0)l++;
			q[++r] = i;
			t[r] = LineCross(lines[q[r]], lines[q[r - 1]]);
		}
		while (r - l > 1 && SideJudge(lines[q[l + 1]], t[r]) < 0)r--;
		t[r + 1] = LineCross(lines[q[l + 1]], lines[q[r]]);
		r++;

		// 统计交点
		l++;
		vector<Point> ans(r - l);
		for (int i = 0; i < ans.size(); ++i) {
			ans[i] = t[i + l + 1];
		}

		return ans;
	}
};

Point oiPs[N*2];
HalfPlane hp;

bool boomJudge(int n, int k) {
	hp.len = 0;
	for (int i = 0; i < n; ++i) {
		hp.addLine(Line(oiPs[i+k+1], oiPs[i]));
	}

	auto keyPoints = hp.run();
	double ans = 0;
	for (int i = 2; i < keyPoints.size(); ++i) {
		ans += cross(keyPoints[i - 1] - keyPoints[0], keyPoints[i] - keyPoints[0]);
	}

	return cmp(ans/2) == 0;
}

void  solve() {
	int n;
	
	while (scanf("%d", &n) ==1 &&n) {
		int a, b;

		for (int i = n - 1; i >= 0; --i) {
			scanf("%d%d", &a, &b);
			oiPs[i] = Point(a, b);
			oiPs[i + n] = oiPs[i];
		}

		int l = 1, r = n-2;

		while (l < r) {
			int mid = (l + r) >> 1;
			if (boomJudge(n, mid)) {
				r = mid;
			}
			else {
				l = mid + 1;
			}
		}

		printf("%d\n", l);
	}
}


int main() {
	solve();
	return 0;

}

/*
3
0 0
50 50
60 10
5
0 0
0 10
10 20
20 10
25 0

*/

本人码农,希望通过自己的分享,让大家更容易学懂计算机知识。创作不易,帮忙点击公众号的链接。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1220274.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何选择适合企业的ERP管理系统?

如何选择适合企业的ERP管理系统&#xff1f; 企业业务不断发展和扩大&#xff0c;ERP管理系统已成为企业实现信息化管理、提高工作效率、降低成本的重要工具。然而&#xff0c;市场上ERP管理系统种类繁多&#xff0c;如何选择适合自己企业的ERP管理系统成为了企业面临的难题。…

陪诊系统定制|陪诊系统实现个性化医疗陪护

随着社会的发展和人口老龄化的加剧&#xff0c;医疗陪诊服务逐渐成为了人们生活中不可或缺的一部分。为了满足这一需求&#xff0c;越来越多的创业者开始关注陪诊小程序的开发。那么&#xff0c;陪诊小程序源码如何开发呢&#xff1f;本文将探讨陪诊系统源码开发的意义、关键功…

java:springboot单元测试spring-boot-starter-test

背景 Java的单元测试可以使用多个框架&#xff0c;其中比较流行的包括&#xff1a; JUnit&#xff1a;JUnit是Java单元测试最常用的框架&#xff0c;它提供了一套丰富的API&#xff0c;可以方便地编写测试用例和测试套件。JUnit 5是JUnit的最新版本&#xff0c;引入了许多新功…

电源模块输出过冲如何产生?测试标准及其测试方法是什么?

输出电压过冲是一个常见的电路问题&#xff0c;它是指在电路中的电压超过了规定的范围&#xff0c;严重的话会造成系统不稳定&#xff0c;导致元器件、负载以及设备损坏。那么造成输出电压过冲的原因有哪些呢&#xff1f;电源自动测试系统要怎么测试输出过冲? 输出电压过冲产生…

DMA covert communication

DMA隐蔽通信的新思路 拆分DMA的RF链 思路的主要来源: article{Ci2021HybridBD, title{Hybrid Beamforming Design for Covert Multicast mmWave Massive MIMO Communications}, author{Wei Ci and Chenhao Qi and Geoffrey Ye Li and Shiwen Mao}, journal{2021 IEEE Global …

项目技术复盘

背景 该项目接手时已是8月中下旬&#xff0c;并且客户要求九月中旬输出第一版本。这么紧急的节奏&#xff0c;不知道商务是如何答应的。临危受命&#xff0c;让我承担开发经理岗位&#xff0c;主导该项目。 开发团队 岗位 人员 base 架构师兼高级软件工程师 季工 上海 高…

Isight 2023 新功能介绍

​​Isight是一个仿真分析流程自动化和多学科多目标优化工具&#xff0c;它提供了一个可视化的灵活的仿真流程搭建平台&#xff0c;同时提供与多种主流CAE分析工具的专用接口&#xff0c;利用此工具&#xff0c;用户可以方便的以拖拽的方式可视化的快速建立复杂的仿真分析流程&…

榜首揭秘:热门产品原型设计工具TOP10

在现代产品制作和创新过程中&#xff0c;原型设计工具起着不可或缺的作用。这些工具为设计师、开发人员和团队提供了更深入的理解&#xff0c;以便核实和呈现他们的设计想法。根据项目的特性&#xff0c;可能需要使用各种类型的工具&#xff0c;因此&#xff0c;我们将在此篇文…

最新随机小姐姐无水印开源版HTML源码

推荐一款高质量的HTML引流源码&#xff0c;适合浏览小姐姐短视频 这款源码具有出色的用户界面设计&#xff0c;给人视觉上的美感 视频播放流畅稳定&#xff0c;给用户带来良好的观影体验 小姐姐们的外貌也非常美丽吸引人 想要浏览精彩的小姐姐短视频&#xff0c;不妨试试这款…

智能位移监测,更新传统井盖的功能

在城市的街道和人行道上&#xff0c;我们经常可以看到井盖的存在。井盖作为地下管道和设施的入口承载着重要的功能。然而过去我们可能忽视了一个重要的问题&#xff1a;井盖的位移可能会对人们产生潜在的威胁。为了保护我们的生活安全和交通畅通无阻和确保城市生命线安全稳定&a…

老师如何在家长群发布通知?

班级查询系统是一种方便快捷的方式&#xff0c;让学生或家长能够随时在线查看老师发布的通知或回执。通过使用各种代码、Excel、小程序工具&#xff0c;老师可以轻松实现学生自助查询班级信息的功能。本文将向老师介绍如何发布成绩查询通知&#xff0c;帮助学生和家长更好地了解…

大势所趋!机器视觉替换传统人工,深眸科技以工业AI视觉赋能生产

如今&#xff0c;在工业4.0的浪潮下&#xff0c;人工智能技术凭借着优化生产流程、实现个性化定制、保障产品安全、促进产业变革等优势&#xff0c;逐渐成为制造业数智化转型的“利器”之一&#xff0c;其在工业生产中的广泛应用使传统制造业焕发生机。 机器视觉作为人工智能快…

非 dict 字典类型的处理

在Python的requests库中&#xff0c;使用data参数发送POST请求时&#xff0c;如果传入的数据对象不是直接继承自dict的字典类型&#xff0c;就会抛出TypeError异常。 Python的requests库是一个广泛用于HTTP请求的库&#xff0c;它提供了丰富的功能来发送和处理HTTP请求。其中&…

Workplace Search 的演变:使用 Elasticsearch 搜索你的私人数据

作者&#xff1a;Dana Juratoni, Aditya Tripathi Workplace Search 功能将来将与 Elastic Search 合并。 这是你需要了解的内容。 生成式人工智能技术的最新进展为搜索带来了一系列可能性。 随着开发人员构建新的体验&#xff0c;用户正在采用新的搜索使用方式 —— 从用自然…

基础课7——数据预处理

在智能客服系统中&#xff0c;数据预处理是进行自然语言处理&#xff08;NLP&#xff09;的关键步骤之一。它是对用户输入的文本数据进行分析、处理和转换的过程&#xff0c;目的是将原始文本数据转化为计算机可理解的语言&#xff0c;为后续的智能回答提供支持。 1.什么是数据…

html在线生成二维码(附源码)

文章目录 1.设计来源1.1 主界面1.2 美化功能 2.效果和源码2.1 动态效果2.2 源代码 源码下载 作者&#xff1a;xcLeigh 文章地址&#xff1a;https://blog.csdn.net/weixin_43151418/article/details/134458927 html二维码生成&#xff08;附源码&#xff09;&#xff0c;生成二…

Cow Lineup S——离散化、单调队列、双指针

题目描述 思路 x、id不大于1亿&#xff0c;数据量太大&#xff0c;使用离散化将id离散化成一串从1开始连续的编号&#xff0c;使用map集合进行离散化使用双指针维护一段区间&#xff0c;这段区间满足每个编号都包含 如何使用map集合进行离散化&#xff1f; 维护一个变量nums…

在Linux上安装Oracle 数据库 11g (含静默方式安装)

好久没碰11g了&#xff0c;今天&#xff08;2023年11月16日&#xff09;因为有个需求又装了一遍。 在OCI上安装了一个Oracle Linux 6实例&#xff1a; $ uname -a Linux instance-20231116-1239-db11g 4.1.12-124.80.1.el6uek.x86_64 #2 SMP Mon Oct 9 02:32:10 PDT 2023 x86…

Springboot升级为3.0.6

版本升级 Springboot升级为3.0.6 版本从原来的2.7.12换成了3.0.6 <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.0.6</version><relativePath/> <…

使用宝塔面板安装mysql

1.第一步 在官网https://www.bt.cn/new/download.html下载页面直接在服务器控制面板复制这里的代码下载即可 2.第二步 下载好后按照服务器面版上有个公网地址&#xff0c;含有用户名和密码&#xff0c;保存好&#xff0c;然后通过公网地址打开一个网页&#xff0c;绑定自己注册…