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

本文介绍了一种判断凸包稳定性的方法,通过比较最大点集和最小点集来验证给出的点集能否唯一确定一个凸包。讨论了三种不同的判断方法,并给出了详细的AC代码。
373

被折叠的 条评论
为什么被折叠?



