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