POJ 1228 Grandpa's Estate(凸包应用:稳定凸包)

558 篇文章 0 订阅
273 篇文章 0 订阅

POJ 1228 Grandpa's Estate(凸包应用:稳定凸包)

http://poj.org/problem?id=1228

题意:

       有n个点,这n个点每个都是凸包上边界的点.问你这n个是否正好能确定一个凸包?

分析:

       n个凸包边界的点正好能确定唯一一个凸包的 充要条件是 这n个点确定的凸包每条边上至少有3个点.

       因为如果当前n个点形成的凸包上,假如有一条边只有两个点,那么你可以添加一个凸包外的点使得该凸包变大,以前的n个点都还是在凸包的边界上.

       判断方法:求出原始n个点的凸包点集.有下面3种方法

1.    对于凸包的每条边(此处用的最小点集),用原始的每个点去判断看该点是否在当前边上.如果某条边上的点数目<3,那么说明该凸包不唯一(本题可用,不会超时).

2.    分别求出凸包的最大点集和最小点集(用的刘汝佳的模板,最大点集是集合中相邻3点可能共线但是集合中的所有点又确实构成了凸包的整个边界. 最小点集是集合中的相邻3点不共线,且使得点集的大小尽量小.最小点集中的点只取顶角的端点,最大点集中的点只要是在凸包边上的点都取) 然后对于最小点集来说,看看最小点集中相邻的两个点之间 在最大点集中是否还存在点. 注意最小点集的点相对顺序与它们在最大点集中的相对顺序一样,只不过在它们之间插入了一些其他点才形成了最大点集.

3.    看边是否共线判断(此处用的最大点集). 假设当前边号为i,它的前一条边为i-1,它的后一条边为i+1. 如果凸包每条边i都或者与i-1共线或与i+1边共线,那么说明该凸包唯一(即凸包每条边上至少3点). 否则只要有一条边i不与边i-1共线且不与边i+1共线,那么该凸包肯定不唯一.(即凸包存在边上点数<3的边).

AC代码:

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
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){}
    bool operator==(const Point& B)const
    {
        return dcmp(x-B.x)==0 && dcmp(y-B.y)==0;
    }
    bool operator<(const Point &B)const
    {
        return dcmp(x-B.x)<0 || (dcmp(x-B.x)==0 && dcmp(y-B.y)<0);
    }
};
typedef Point Vector;
Vector operator-(Point A,Point B)
{
    return Vector(A.x-B.x,A.y-B.y);
}
double Cross(Vector A,Vector B)
{
    return A.x*B.y-A.y*B.x;
}
int ConvexHull_1(Point *p,int n,Point *ch)//点数最少的凸包
{//该凸包的点集任意3点不会共线
    sort(p,p+n);
    n=unique(p,p+n)-p;
    int m=0;
    for(int i=0;i<n;i++)
    {
        while(m>1 && Cross(ch[m-1]-ch[m-2], p[i]-ch[m-2])<=0) m--;//注意<=0
        ch[m++]=p[i];
    }
    int k=m;
    for(int i=n-2;i>=0;i--)
    {
        while(m>k && Cross(ch[m-1]-ch[m-2], p[i]-ch[m-2])<=0) m--;
        ch[m++]=p[i];
    }
    if(n>1) m--;
    return m;
}
int ConvexHull_2(Point *p,int n,Point *ch)//点数最多的凸包
{//该凸包的点集ch中可能相邻3点共线
    sort(p,p+n);
    n=unique(p,p+n)-p;
    int m=0;
    for(int i=0;i<n;i++)
    {
        while(m>1 && Cross(ch[m-1]-ch[m-2], p[i]-ch[m-2])<0) m--;//注意<0
        ch[m++]=p[i];
    }
    int k=m;
    for(int i=n-2;i>=0;i--)
    {
        while(m>k && Cross(ch[m-1]-ch[m-2], p[i]-ch[m-2])<0) m--;
        ch[m++]=p[i];
    }
    if(n>1) m--;
    return m;
}
bool check(Point *ch1,int m1,Point *ch2,int m2)//判定凸包是否稳定
{//判定ch1点集中的任意相邻两点构成的边之间是否存在另外一个点
    int j;
    for(j=0;j<m2;j++)
        if(ch1[0]==ch2[j]) break;
    for(int i=0;i<m1;i++)
    {
        bool flag=false;
        j=(j+1)%m2;
        while(!(ch2[j]==ch1[(i+1)%m1]) )
        {
            j=(j+1)%m2;
            flag=true;
        }
        if(flag==false) return false;
    }
    return true;
}
const int maxn=1000+5;
Point p[maxn],ch1[maxn],ch2[maxn];
int main()
{
    int T; scanf("%d",&T);
    while(T--)
    {
        int n;
        scanf("%d",&n);
        for(int i=0;i<n;i++)
            scanf("%lf%lf",&p[i].x,&p[i].y);
        if(n<6)
        {
            printf("NO\n");
            continue;
        }
        int m1=ConvexHull_1(p,n,ch1);
        int m2=ConvexHull_2(p,n,ch2);

        bool ok=check(ch1,m1,ch2,m2);
        printf("%s\n",ok?"YES":"NO");
    }
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值