POJ 1228 Grandpa's Estate 稳定凸包

http://poj.org/problem?id=1228
Description
Being the only living descendant of his grandfather, Kamran the Believer inherited all of the grandpa’s belongings. The most valuable one was a piece of convex polygon shaped farm in the grandpa’s birth village. The farm was originally separated from the neighboring farms by a thick rope hooked to some spikes (big nails) placed on the boundary of the polygon. But, when Kamran went to visit his farm, he noticed that the rope and some spikes are missing. Your task is to write a program to help Kamran decide whether the boundary of his farm can be exactly determined only by the remaining spikes.

Input
The first line of the input file contains a single integer t (1 <= t <= 10), the number of test cases, followed by the input data for each test case. The first line of each test case contains an integer n (1 <= n <= 1000) which is the number of remaining spikes. Next, there are n lines, one line per spike, each containing a pair of integers which are x and y coordinates of the spike.

Output
There should be one output line per test case containing YES or NO depending on whether the boundary of the farm can be uniquely determined from the input.

Sample Input

1
6
0 0
1 2
3 4
2 0
2 4
5 0

Sample Output

NO

Source
Tehran 2002 Preliminary
题目大意:给出一堆点,问这些点能不能构成一个唯一的凸包。(稳定凸包)
思路:当凸包上存在一条边上的点只有端点两个点的时候,这个凸包不是稳定的,因为它可以在这条边外再引入一个点,构成一个新的凸包。但一旦一条边上存在三个点,那么不可能再找到一个点使它扩展成一个新的凸包。那么一个稳定凸包最少有 6 6 6个点(构成一个三角形)。若给定的 n &gt; = 6 n&gt;=6 n>=6我们就用给定的点构成一个凸包,然后看每条边上是不是至少有三个点(因此这个凸包是允许共线点的 可能要修改一下凸包的模板)。判断三点共线比较简单,用叉积就行了,判断 ( B ⃗ − A ⃗ ) × ( C ⃗ − A ⃗ ) (\vec{B}-\vec{A})\times(\vec{C}-\vec{A}) (B A )×(C A )是否等于0。但是我们不能仅判断相邻的三个点,因为这在凸包的转折点是不成立的。应该判断相邻的5个点,具体见代码。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<vector>
#include<algorithm>
using namespace std;

const double pi=acos(-1);//弧度pi
const double eps=1e-8;//精度

inline int sgn(double x)
{
    if(fabs(x)<eps)
        return 0;
    if(x>0)
        return 1;
    return -1;
}

struct point
{
    double x,y;
    point(double a=0,double b=0)
    {
        x=a,y=b;
    }
    friend point operator * (point a,double b)
    {
        return point(a.x*b,a.y*b);
    }
    friend point operator * (double a,point b)
    {
        return point(b.x*a,b.y*a);
    }
    point operator - (const point &b)const
    {
        return point(x-b.x,y-b.y);
    }
    point operator + (const point &b)const
    {
        return point(x+b.x,y+b.y);
    }
    point operator / (const double b)const
    {
        return point(x/b,y/b);
    }
    bool operator < (const point &b)const//按坐标排序
    {
        if(fabs(x-b.x)<eps)
            return y<b.y-eps;
        return x<b.x-eps;
    }
    bool operator == (const point &b)const
    {
        return sgn(x-b.x)==0&&sgn(y-b.y)==0;
    }
    void transxy(double sinb,double cosb)//逆时针旋转b弧度
    {                                      //若顺时针 在传入的sinb前加个-即可
        double tx=x,ty=y;
        x=tx*cosb-ty*sinb;
        y=tx*sinb+ty*cosb;
    }
    void transxy(double b)//逆时针旋转b弧度
    {                     //若顺时针传入-b即可
        double tx=x,ty=y;
        x=tx*cos(b)-ty*sin(b);
        y=tx*sin(b)+ty*cos(b);
    }
    double norm()
    {
        return sqrt(x*x+y*y);
    }
};

inline double dot(point a,point b)//点积
{
    return a.x*b.x+a.y*b.y;
}
inline double cross(point a,point b)//叉积
{
    return a.x*b.y-a.y*b.x;
}

inline double dist(point a,point b)//两点间距离
{
    return (a-b).norm();
}

typedef point Vector;

vector<point> a;

struct polygon_convex//凸包
{
    vector<point> p;
    polygon_convex(int siz=0)
    {
        p.resize(siz);
    }
    double perimeter()//计算多边形周长
    {
        double sum=0;
        int len=p.size();
        for(int i=0;i<len-1;i++)
            sum+=(p[i+1]-p[i]).norm();
        sum+=(p[0]-p[len-1]).norm();
        return sum;
    }
    int contain(const point b)//lgn的复杂度下判断点b是否在凸包内 true表示在凸包内(或边界上)
    {
        int n=p.size();
        point g=(p[0]+p[n/3]+p[2*n/3])/3.0;
        int l=0,r=n,mid;//二分凸包
        while(l+1<r)
        {
            mid=(l+r)>>1;
            if(sgn(cross(p[l]-g,p[mid]-g))>0)
            {
                if(sgn(cross(p[l]-g,b-g))>=0&&sgn(cross(p[mid]-g,b-g))<0)
                    r=mid;
                else
                    l=mid;
            }
            else
            {
                if(sgn(cross(p[l]-g,b-g))<0&&sgn(cross(p[mid]-g,b-g))>=0)
                    l=mid;
                else
                    r=mid;
            }
        }
        r%=n;
        int z=sgn(cross(p[r]-b,p[l]-b))-1;
        if(z==-2)
            return 1;
        return z;
    }
};

polygon_convex convex_hull()//用a中的点求解出凸包
{
    polygon_convex res(2*a.size()+5);
    sort(a.begin(),a.end());//按照横坐标排序
    a.erase(unique(a.begin(),a.end()),a.end());//去重
    int m=0;
    int len=a.size();
    for(int i=0;i<len;i++)//求下凸包
    {
        while(m>1&&sgn(cross(res.p[m-1]-res.p[m-2],a[i]-res.p[m-2]))<0)//不包括共线点 如果包括共线点请修改此处的<=为<
            --m;
        res.p[m++]=a[i];
    }
    int k=m;
    for(int i=len-2;i>=0;i--)//求上凸包
    {
        while(m>k&&sgn(cross(res.p[m-1]-res.p[m-2],a[i]-res.p[m-2]))<0)//不包括共线点 如果包括共线点请修改此处的<=为<
            --m;
        res.p[m++]=a[i];
    }
    res.p.resize(m);
    if(len>1)//去重
        res.p.resize(m-1);
    return res;
}

int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n;
        point tmp;
        scanf("%d",&n);
        a.resize(n);
        for(int i=0;i<n;i++)
        {
            scanf("%lf%lf",&tmp.x,&tmp.y);
            a[i]=tmp;
        }
        if(n<6)
            printf("NO\n");
        else
        {
            polygon_convex pc=convex_hull();
            pc.p.push_back(pc.p[0]);//首尾相连
            bool flag=0;
            int len=pc.p.size();
            for(int i=1;i<len-2;i++)
            {
                if(sgn(cross(pc.p[i]-pc.p[i-1],pc.p[i+1]-pc.p[i-1]))!=0&&sgn(cross(pc.p[i+1]-pc.p[i],pc.p[i+2]-pc.p[i]))!=0)
                    flag=1;
                if(flag)
                    break;
            }
            printf("%s\n",flag==1?"NO":"YES");
        }
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值