UVA 1475 Jungle Outpost(二分+半平面交)
http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=4221
题意:
在丛林中有n个瞭望台,形参了一个凸n多边形.这些瞭望台的保护范围就是这个凸多边形内的任意点. 敌人进攻时,会炸掉一些瞭望台,使得总部暴露在那些剩下的瞭望台的凸包之外.你的任务是选一个点作为总部,使得敌人需要炸坏的瞭望台数目尽量多. n个瞭望台按顺时针顺序输入.
分析: 刘汝佳<<训练指南>> P281例题12
如果敌人只有一颗炸弹,炸掉一个顶点后,不会暴露在外面的是一条有向直线的”左边”. 每条直线代表一个半平面,这样所有的这种直线就形成了一个半平面交. 我们就要看这个交集是否是非空的了.
如果敌人有多个炸弹,他应该怎么炸才是最优呢? 这多个炸弹肯定是炸毁连续的几个顶点才最好.(自己画图比较一下) 所以这些炸弹又形成了一个行的半平面,我们依然看所有半平面是否存在交集即可.
如果存在交集,说明敌人炸弹不够,还要炸更多的瞭望台才能使得我们在任何地方建立总部都会被暴露.
所以我们二分敌人炸弹的数目mid,然后对于每个mid来构建半平面.求半平面的交集,看交集是否为空即可.
AC代码:
#include<cstdio> #include<cstring> #include<algorithm> #include<cmath> using namespace std; //精度控制 const double eps=1e-10; int dcmp(double x) { if(fabs(x)<eps) return 0; return x<0?-1:1; } //点 struct Point { double x,y; Point(){} Point(double x,double y):x(x),y(y){} }; //向量 typedef Point Vector; //点-点==向量 Vector operator-(Point A,Point B) { return Vector(A.x-B.x,A.y-B.y); } //向量+向量==向量 Vector operator+(Vector A,Vector B) { return Vector(A.x+B.x,A.y+B.y); } //向量*实数==向量 Vector operator*(Vector A,double p) { return Vector(A.x*p, A.y*p); } //叉积 double Cross(Vector A,Vector B) { return A.x*B.y-A.y*B.x; } //有方向的直线 struct Line { Point p; Vector v; double ang; Line(){} Line(Point p,Vector v):p(p),v(v) { ang=atan2(v.y,v.x); } bool operator<(const Line &L)const { return ang<L.ang; } }; //判断点p是否在直线L左边 bool OnLeft(Line L,Point p) { return Cross(L.v,p-L.p)>0; } //得到a与b两直线的交点 Point GetIntersection(Line a,Line b) { Vector u=a.p-b.p; double t=Cross(b.v,u)/Cross(a.v,b.v); return a.p+a.v*t; } //返回半平面交的凸多边形poly节点集合 int HalfplaneIntersection(Line *L,int n,Point *poly) { sort(L,L+n); int first=0,last=0; Point *p=new Point[n]; Line *q=new Line[n]; q[0]=L[0]; for(int i=1;i<n;i++) { while(first<last && !OnLeft(L[i],p[last-1])) last--; while(first<last && !OnLeft(L[i],p[first])) first++; q[++last]=L[i]; if(fabs(Cross(q[last].v,q[last-1].v))<eps) { last--; if(OnLeft(q[last],L[i].p)) q[last]=L[i]; } if(first<last) p[last-1]=GetIntersection(q[last-1],q[last]); } while(first<last && !OnLeft(q[first],p[last-1])) last--; if(last-first<=1 ) return 0; p[last]=GetIntersection(q[last],q[first]); int m=0; for(int i=first;i<=last;i++) poly[m++]=p[i]; return m; } /***以上为刘汝佳模板***/ const int maxn=50000+5; Point p[maxn],poly[maxn]; Line L[maxn]; int main() { int n; while(scanf("%d",&n)==1) { for(int i=0;i<n;i++) scanf("%lf%lf",&p[i].x,&p[i].y); if(n==3)//三角形的话,直接删除一个点即可 { printf("1\n"); continue; } reverse(p,p+n);//将所有顶点逆时针存放 //当炸掉n-2个连续的点时,半平面交肯定是空 int left=1,right=n-2; while(right>left) { int mid=left+(right-left)/2; for(int i=0;i<n;i++) L[i]=Line(p[i],p[(i+1+mid)%n]-p[i]); int m=HalfplaneIntersection(L,n,poly); //m>0表示半平面交非空,那么需要加炸弹 if(m>0) left=mid+1; else right=mid; } printf("%d\n",left); } return 0; }