UVA 1475 Jungle Outpost(二分+半平面交)

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

你可能感兴趣的:(Algorithm,算法,ACM,计算几何)