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