贪心半平面求交 - 洛谷 - P2600 [ZJOI2008] 瞭望塔

news2024/9/22 15:16:11

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

往期相关背景半平面求交 点击前往
凸包点击前往

题目大意

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

有一座山,可以用一条山的上方轮廓折线(x1, y1), (x2, y2), …. (xn, yn)来描述H村的形状,这里x1 < x2 < …< xn。瞭望塔可以建造在[x1, xn]间的任意位置, 但必须满足从瞭望塔的顶端可以看到山的任意位置。可见在不同的位置建造瞭望塔,所需要建造的高度是不同的。为了节省开支,村长希望建造的塔高度尽可能小。

解析

在这里插入图片描述
根据题意,可以看到山的任意地方的区域为所有山坡所在直线的半平面求交区域。即图中的黄色区域。2条红线是根据题意添加的辅助边,因为塔只能建在x1到xn以内。

现在要求塔的高度最小,也就是求黄色区域最下部分与山的最上部分之差最小。可以通过枚举黄色区域下方顶点找一遍,再通过山的顶点找一遍。

特殊情况只有1个点的时候直接返回0即可。

代码


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


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

const int N = 2e5 + 10;

namespace FloatSys {
	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("%.3f %.3f\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 cross(const Point& a, const Point& b, const Point& c) {
		return cross(b - a, c - a);
	}

	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;
		int u, v;
		Line() {}
		Line(const Point& a, const Point& b) :front(a), tail(b) {
			ang = atan2(front.y - tail.y, front.x - tail.x);
		}
		void initAng() {
			ang = atan2(front.y - tail.y, front.x - tail.x);
		}
	};

	int cmp(const Line& a, const Line& b) {
		//if (a.u == b.u && a.v == b.v)return 0;
		return cmp(a.ang - b.ang);

	}


	// 点在直线哪一边>0 左边,<0边
	double SideJudge(const Line& a, const Point& b) {
		return cmp(cross(a.front - a.tail, b - a.tail));
		//return 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	cross(b.front - b.tail, a.front - b.tail) > 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, bool& oneline) {
		// 计算 (q-p)Xr
		auto qpr = cross(q - p, r);
		auto qps = cross(q - p, s);

		auto rXs = cross(r, s);
		if (cmp(rXs) == 0) {
			oneline = true;
			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, bool& f) {
		Point dira = a.front - a.tail;
		Point dirb = b.front - b.tail;
		bool oneline = false;
		auto p = intersection(a.tail, dira, b.tail, dirb, oneline);
		if (oneline)f = false;
		return a.tail + dira * p.first;
	}


	class HalfPlane {
	public:
		vector<Line> lines;

		void addLine(const Line& a) {
			lines.push_back(a);
		}

		vector<Point> run() {
			sort(lines.begin(), lines.end(), LineSort);
			vector<int> q(lines.size() + 10);
			vector<Point> t(lines.size() + 10);

			int l = -1, r = 0;
			q[0] = 0;
			for (int i = 1; i < lines.size(); ++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;
				bool f = true;
				t[r] = LineCross(lines[q[r]], lines[q[r - 1]], f);
			}
			while (r - l > 1 && SideJudge(lines[q[l + 1]], t[r]) < 0)r--;
			/*if (r - l > 1) {
				bool f = true;
				t[r + 1] = LineCross(lines[q[l + 1]], lines[q[r]], f);
				r++;
			}*/

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

			return ans;
		}
	};
}



// 枚举p1,求取所有高度最低点
double getMinHigh(const vector<FloatSys::Point> &p1, const vector<FloatSys::Point>& p2) {
	double minHigh = abs(p1[0].y - p2[0].y);

	for (int j = 0, i = 1; i < p1.size(); ++i) {
		while (p2[j + 1].x < p1[i].x)++j;
		double p2y = (p1[i].x-p2[j].x) / (p2[j + 1].x - p2[j].x) * (p2[j + 1].y - p2[j].y) + p2[j].y;
		minHigh = min(minHigh, abs(p2y - p1[i].y));
	}

	return minHigh;
}


void  solve() {
	using namespace FloatSys;
	int n;
	scanf("%d", &n);

	vector<Point> tangs(n);
	int a;
	for (int i = 0; i < n; ++i) {
		scanf("%d", &a);
		tangs[i].x = a;
	}
	for (int i = 0; i < n; ++i) {
		scanf("%d", &a);
		tangs[i].y = a;
	}

	if (n < 2) {
		puts("0.000");
		return;
	}

	HalfPlane hp;
	// 添加辅助线
	hp.addLine({ tangs[0], tangs[0] + Point(0, 1)});
	hp.addLine({ tangs[n-1]+Point(0,1), tangs[n-1]});

	for (int i = 0; i < n - 1; ++i) {
		hp.addLine({ tangs[i + 1], tangs[i] });
	}

	vector<Point> ps = hp.run();
	sort(ps.begin(), ps.end());


	printf("%.3f\n", min(getMinHigh(tangs, ps), getMinHigh(ps, tangs)));
}


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

}

/*
6
1 2 4 5 6 7
1 2 2 4 2 1


2
1 2
*/

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

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

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

相关文章

数据采集有哪些方法?HTTP代理起到什么作用?

在这个数字化的时代&#xff0c;数据就如同生活中不可或缺的元素&#xff0c;我们的行为、喜好、甚至是想法都被转化成了数字化的信息。那么&#xff0c;现代社会是如何进行数据的采集的呢&#xff1f;让我们一同来看看&#xff01; 1. 网络浏览行为的追踪 在我们浏览互联网的…

nacos 2.* 部署在linux服务器无法注册问题

通过sdk注册代码 报错 Exception in thread "main" ErrCode:-401, ErrMsg:Client not connected, current status:STARTING at com.alibaba.nacos.common.remote.client.RpcClient.request(RpcClient.java:639) at com.alibaba.nacos.common.remote.client…

Python基础篇: 环境安装

Python基础环境使用 一&#xff1a;运行环境Anaconda介绍1、Anaconda搭建1.1、下载方式1.2、安装1.3、验证是否安装成功 2、管理python环境2.1、列出所有环境2.2、创建环境2.3、进入指定虚拟环境2.4、离开虚拟环境2.5、删除虚拟环境 3、依赖管理3.1、安装依赖3.2、卸载依赖3.3、…

关于电气火灾监控产品在石化行业的应用探讨-安科瑞 蒋静

摘 要&#xff1a;通过对石油化工企业电气火灾成因的分析,针对目前剩余电流检测与故障电弧检测手段在石油化工企业应用中存在的问题&#xff0c;提出一种智慧防火解决方案。通过对各类电气火灾隐患进行集中监测&#xff0c;提高了电气火灾隐患检测的精度&#xff0c;从而达到避…

Springboot+vue的工作流程管理系统(有报告),Javaee项目,springboot vue前后端分离项目

演示视频&#xff1a; Springbootvue的工作流程管理系统(有报告)&#xff0c;Javaee项目&#xff0c;springboot vue前后端分离项目 项目介绍&#xff1a; 本文设计了一个基于Springbootvue的前后端分离的工作流程管理系统&#xff0c;采用M&#xff08;model&#xff09;V&am…

HarmonyOS 应用开发学习笔记 状态管理概述

移动端开发&#xff0c;最重要的一点就是数据的处理&#xff0c;并且正确的显示渲染UI。 变量在页面和组件、组件和组件之间有时候并不能实时共享&#xff0c;而有时候&#xff0c;又不需要太多的作用域&#xff08;节省资源&#xff09;&#xff0c;作用就需要根据不同场景&am…

bootstrap5实现宠物商店网站 Cat-Master

一、需求分析 宠物商店网站是指专门为宠物商店或宠物用品商家而建立的在线平台。这种网站的功能通常旨在提供以下服务&#xff1a; 产品展示&#xff1a;宠物商店网站通常会展示宠物食品、玩具、床上用品、健康护理产品等各种宠物用品的图片和详细信息。这样&#xff0c;潜在的…

欧科云链研究院:奔赴2024,Web3与AI共振引爆数字时代潘多拉魔盒

出品&#xff5c;欧科云链研究院 2024年&#xff0c;Web3与AI两个数字科技的巅峰碰撞&#xff0c;欧科云链研究院探索AI与Web3的技术融合&#xff0c;与澎湃科技联合发布2024年展望&#xff0c;原标题为《2024年展望&#xff1a;Web3与AI共振引爆可信数字社会》&#xff0c;共…

Proxmox VE 超融合集群销毁Ceph Pool

作者&#xff1a;田逸&#xff08;formyz&#xff09; 销毁Ceph Pool的目的 一套五节点的Proxmox VE超融合集群&#xff0c;当初为有效利用资源&#xff0c;配备了Nvme高性能磁盘和大容量的SATA机械磁盘&#xff08;如图所示&#xff09;&#xff0c;高性能Nvme磁盘用于虚拟机…

阿里云域名优惠口令2024年更新,注册、续费和转入可用

2024年阿里云域名优惠口令&#xff0c;com域名续费优惠口令“com批量注册更享优惠”&#xff0c;cn域名续费优惠口令“cn注册多个价格更优”&#xff0c;cn域名注册优惠口令“互联网上的中国标识”&#xff0c;阿里云优惠口令是域名专属的优惠码&#xff0c;可用于域名注册、续…

docker +gitee+ jenkins +maven项目 (二)

文章目录 前言一、创建Maven项目二、常规配置1.gitee配置2.gitee仓库配置3.构建时操作4.构建后操作 总结 前言 上一篇文章介绍了Jenkins的环境配置和工具配置&#xff0c;这篇进行具体maven项目的配置 一、创建Maven项目 二、常规配置 1.gitee配置 在工具哪里配置好gitee后&…

element-ui table height 属性导致界面卡死

问题: 项目上&#xff0c;有个点击按钮弹出抽屉的交互, 此时界面卡死 原因分析: 一些场景下(父组件使用动态单位/弹窗、抽屉中使用), element-ui 的 table 会循环计算高度值, 导致界面卡死 github 上的一些 issues 和解决方案: Issues ElemeFE/element GitHub 官方讲是升…

pyfolio工具结合backtrader分析量化策略组合,附源码+问题分析

pyfolio可以分析backtrader的策略&#xff0c;并生成一系列好看的图表&#xff0c;但是由于pyfolio直接install的稳定版有缺陷&#xff0c;开发版也存在诸多问题&#xff0c;使用的依赖版本都偏低&#xff0c;试用了一下之后还是更推荐quantstats。 1、安装依赖 pip install …

vue3中使用echarts:tooltip的trigger为axis tooltip不显示问题

vue3中使用echarts时&#xff0c;tooltip的trigger设置为axis时formatter不触发 tooltip: {trigger: "axis",formatter: function (params) {console.log("params", params);},axisPointer: {type: "shadow", // 阴影指示器}, },解决办法&#…

【Turtle库】海绵宝宝

在这个充满创意与想象力的时代&#xff0c;我们决定挑战一项富有意义且有趣的使命——使用Python编程语言绘制海绵宝宝。这个经典的角色&#xff0c;以其独特的魅力和无与伦比的搞笑天赋&#xff0c;已经成为了无数人心中的童年回忆。现在&#xff0c;我们希望通过Python的强大…

4《数据结构》

文章目录 绪论逻辑结构存储结构【物理结构】顺序和链式存储区别顺序表和数组区别数组和链表的区别链表结点概念链表为空条件链表文章http://t.csdnimg.cn/dssVK二叉树B树B树【MYSQL索引默认数据结构】B树和B树区别冒泡排序插排选排快排 绪论 数据结构&#xff1a;研究非数值计…

openFeign服务调用

简介 Feign是一个声明式WebService客户端。使用Feign能让编写Web Service客户端更加简单。它的使用方法是定义一个服务接口然后在上面添加注解。 Feign也支持可拔插式的编码器和解码器。Spring Cloud对Feign进行了封装&#xff0c;使其支持了Spring MVC标准注解和HttpMessageC…

数据库——SQL注入攻击

【实验内容及要求】 一、内容&#xff1a;掌握SQL注入攻击的原理&#xff0c;掌握基本SQL注入攻击的方法&#xff0c;掌握防SQL注入攻击的基本措施。 二、要求&#xff1a; 1. DVWA环境配置 DVWA&#xff08;Damn Vulnerable Web Application&#xff09;是一个用来进行安全…

IPD-PDP产品开发流程-PDT产品开发计划Charter文档模板(word)6

今天继续为您分享PDT的产品开发计划Charter模板的内容。 Charter任务书模板内容17&#xff1a;配置管理 项目的配置管理活动应该按照配置管理计划来执行。配置管理计划包括定义项目中的配置项&#xff0c;配置项中需要进行正式变更控制的内容&#xff0c;并为这些配置项和内容…

DS|静态查找

题目一&#xff1a;DS静态查找 -- 顺序查找 题目描述&#xff1a; 给出一个队列和要查找的数值&#xff0c;找出数值在队列中的位置&#xff0c;队列位置从1开始 要求使用带哨兵的顺序查找算法 输入要求&#xff1a; 第一行输入n&#xff0c;表示队列有n个数据 第二行输入…