[P2600][ZJOI2008]瞭望塔(半平面交)

本文介绍了一种几何算法,通过构建封闭多边形并利用半平面交求解瞭望塔的最佳放置区域。文章详细解释了算法流程,包括如何确定拐点和计算最小距离,适用于解决特定类型的几何问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

先在一个无穷高的位置画一条线为上界,把左右两端点向上连,形成一个封闭的多边形,求半平面交找到瞭望塔顶可以在的区域。因为点和点都是以直线连接的,斜率固定,所以半平面交到底边距离最近的点一定是某个拐点。那么把得到的集合内的线两两求一下交点,在从左到右依次求半平面交或是底边上的拐点到另一边的距离,找出最小值就可以了。

#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
const int N=310;
struct node{
	double x,y;
}p1[N],p2[N];
struct edge{
	node s,t;
	double x,y;
}l1[N];
struct edge2{
	double x,y,k,b;
}l2[N];
double ans;
int n,l,r,q1[N],cnt,cnt2;
inline double cross(node a,node b,node c){
	return (b.x-a.x)*(c.y-a.y)-(c.x-a.x)*(b.y-a.y);
}
bool cmp(edge i,edge j){
	if(atan2(i.y,i.x)!=atan2(j.y,j.x))return atan2(i.y,i.x)<atan2(j.y,j.x);
	return cross(i.s,i.t,j.t)<0;
}
bool cmp2(node i,node j){
	return i.x<j.x;
}
inline bool rit(edge a,edge b,edge c){
	node p;double k1,k2,b1,b2;
	if(b.x==0)k2=c.y/c.x,b2=c.s.y-k2*c.s.x,p.x=b.s.x,p.y=p.x*k2+b2;
	else if(c.x==0)k1=b.y/b.x,b1=b.s.y-k1*b.s.x,p.x=c.s.x,p.y=p.x*k1+b1;
	else
		k1=b.y/b.x,k2=c.y/c.x,b1=b.s.y-k1*b.s.x,b2=c.s.y-k2*c.s.x,
		p.x=(b2-b1)/(k1-k2),p.y=p.x*k1+b1;
	return cross(a.s,a.t,p)<0;
}
inline void getpt(edge b,edge c){
	++cnt;double k1,k2,b1,b2;
	if(b.x==0)k2=c.y/c.x,b2=c.s.y-k2*c.s.x,p2[cnt].x=b.s.x,p2[cnt].y=p2[cnt].x*k2+b2;
	else if(c.x==0)k1=b.y/b.x,b1=b.s.y-k1*b.s.x,p2[cnt].x=c.s.x,p2[cnt].y=p2[cnt].x*k1+b1;
	else
		k1=b.y/b.x,k2=c.y/c.x,b1=b.s.y-k1*b.s.x,b2=c.s.y-k2*c.s.x,
		p2[cnt].x=(b2-b1)/(k1-k2),p2[cnt].y=p2[cnt].x*k1+b1;
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;++i)scanf("%lf",&p1[i].x);
	for(int i=1;i<=n;++i)scanf("%lf",&p1[i].y);
	for(int i=1;i<n;++i){
		l1[i].s=p1[i],l1[i].t=p1[i+1],l1[i].x=p1[i+1].x-p1[i].x,l1[i].y=p1[i+1].y-p1[i].y;
		l2[i].x=p1[i+1].x,l2[i].y=p1[i+1].y,l2[i].k=(l1[i].y/l1[i].x),l2[i].b=l2[i].y-l2[i].k*l2[i].x;
	}
	p1[n+1].x=p1[n].x,p1[n+1].y=1e16,p1[n+2].x=p1[1].x,p1[n+2].y=1e16;
	l1[n].s=p1[n],l1[n].t=p1[n+1],l1[n].x=0,l1[n].y=p1[n+1].y-p1[n].y;
	l1[n+1].s=p1[n+1],l1[n+1].t=p1[n+2],l1[n+1].x=p1[n+2].x-p1[n+1].x,l1[n+1].y=p1[n+2].y-p1[n+1].y;
	l1[n+2].s=p1[n+2],l1[n+2].t=p1[1],l1[n+2].x=0,l1[n+2].y=p1[1].y-p1[n+2].y;
	sort(l1+1,l1+n+3,cmp);
	q1[1]=1,l=r=1;
	for(int i=2;i<=n+2;++i){
		if(atan2(l1[i].y,l1[i].x)==atan2(l1[i-1].y,l1[i-1].x))continue;
		while(l<r&&rit(l1[i],l1[q1[r]],l1[q1[r-1]]))--r;
		while(l<r&&rit(l1[i],l1[q1[l]],l1[q1[l+1]]))++l;
		q1[++r]=i;
	}
	while(l<r&&rit(l1[q1[l]],l1[q1[r]],l1[q1[r-1]]))--r;
	while(l<r&&rit(l1[q1[r]],l1[q1[l]],l1[q1[l+1]]))++l;
	cnt=0;
	for(int i=l;i<r;++i)getpt(l1[q1[i]],l1[q1[i+1]]);
	if(l<r)getpt(l1[q1[l]],l1[q1[r]]);
	sort(p2+1,p2+cnt+1,cmp2);
	ans=1e16;cnt2=1;
	while(l2[cnt2].x<=p2[1].x)++cnt2;
	for(int i=1;i<=cnt;++i){
		while(cnt2<n&&p2[i].x>=l2[cnt2].x)
		ans=min(ans,(p2[i-1].y*(p2[i].x-l2[cnt2].x)+p2[i].y*(l2[cnt2].x-p2[i-1].x))/(p2[i].x-p2[i-1].x)-l2[cnt2].y),++cnt2;
		ans=min(ans,p2[i].y-(p2[i].x*l2[cnt2].k+l2[cnt2].b));
	}
	printf("%.3lf",ans);
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值