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;
}