给你n个点,你的工作就是要你指出由这n个点是否能够成唯一的凸多边形,如果能输出YES,否则NO!
题目分析:显然这个题是计算几何问题,要解决本题的关键在于找到由这n个点构成的凸包,当然问题没那么简单,只要求一个凸包完了。第二步的工作在于确定这个凸包是否是唯一的。解决第二步的工作的方法肯定有很多种,在这里只介绍本人的想法。
题目实现:求凸包这里是使用了Graham扫描算法,对各个点相对于左下角的那个点p0排了极角序(具体实现详见程序)。而对于唯一性的判断关键在于一点:凸包的每一条边都至少有n个点中的三个(包括两个端点)(而且没有两个点是重复的)。因为如果凸包的一条边只含有两个端点(在n个点中),那么就有可能在这条直线的另一侧(凸包外面),存在一个点使得这个这个凸包不唯一(边界纠纷)!由于对于所有点都排了极角序,这个问题的解决就有所缓解。只需要检查在任一条凸包的边上至少存在一个点(n中的)在这条边上&&不和两个端点重合即可。如果满足这些条件则输出YES,否则输出NO。
//
// File: pku1228gxGrandpa's Estate.cc
// Author: Administrator
//
// Created on 2009年6月12日, 上午8:18
//
#include <stdlib.h>
#include<math.h>
#include<stdio.h>
#include<iostream>
using namespace std;
//
//
//
#define eps 1e-10
#define zero(x) (((x)>0?(x):-(x))<eps)
struct point{
double x,y;
};
point a[1005],result[1005],wast[1005];//wast 保存不在凸包上的点
double dis(point a,point b){
return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
double xmult(point b,point c,point a){
return (b.x-a.x)*(c.y-a.y)-(b.y-a.y)*(c.x-a.x);
}
int cmp(const void *p1,const void*p2){
point *p3=(point *)p1,*p4=(point *)p2;
double m=xmult(*p3,*p4,a[0]);
if(m<0)return 1;
else if(m==0&&dis(a[0], *p3)<dis(a[0], *p4))return 1;
return -1;
}
void firstpoint(int n){
int i,I=0;
for(i=1;i<n;i++){
if(a[i].y<a[I].y)I=i;
else if(a[i].y==a[I].y&&a[i].x<a[I].x)I=i;
}
point t=a[0];a[0]=a[I];a[I]=t;
}
int tubao(point *a,int n,int &num){
int i,flag=0,top=2;
num=0;
result[0]=a[n]=a[0];
result[1]=a[1];
result[2]=a[2];
for(i=3;i<=n;i++){
while(xmult(result[top],a[i],result[top-1])<=0){
wast[++num]=result[top--];// top点被弹出凸包保存在非凸包点数组里
if(top==1)break;
}
result[++top]=a[i];
}
return top;
}
int dot_online_in(point p,point l1,point l2){//点在线段上包括端点
return
zero(xmult(p,l1,l2))&&(l1.x-p.x)*(l2.x-p.x)<eps&&(l1.y-p.y)*(l2.y-p.y)<eps;
}
int dot_online_ex(point p,point l1,point l2){//点在线段内
return
dot_online_in(p,l1,l2)&&(!zero(p.x-l1.x||!zero(p.y-l1.y))&&(!zero(p.x-l2.x)||!zero(p.y-l2.y)));
}
int main(int argc, char** argv) {
int i,j,T=89,n,flag0,flag,num;
cin>>T;
while(T--){
cin>>n;
for(i=0;i<n;i++)scanf("%lf%lf",&a[i].x,&a[i].y);
if(n<=5){printf("NO/n");continue;}
flag=0;
for(i=2;i<n;i++){
if(!zero(xmult(a[i],a[i-1],a[i-2]))) flag=1;
}
if(flag==0){printf("NO/n");continue;}
firstpoint(n);
qsort(a+1,n-1,sizeof(a[0]),cmp);//极角排序
int N=tubao(a,n,num);
/*
printf("/n");
for(i=0;i<=N;i++)
printf("%.0lf %.0lf/n",result[i].x,result[i].y);
*/
/*
printf("/n");
for(i=1;i<=num;i++)
printf("%.0lf %.0lf/n",wast[i].x,wast[i].y); printf("/n");
*/
flag0=1;
for(i=1;i<=N;i++){
flag=0;
for(j=1;j<=num;j++){
//if(dot_online_ex(a[j],result[i-1],result[i])){ //你也可以把所有的点比较一遍
if(dot_online_ex(wast[j],result[i-1],result[i])){//一条凸包边上是否有点存在
flag=1;break;
}
}
if(flag==0){flag0=0;break;}
}
if(flag0==1)printf("YES/n");
else printf("NO/n");
}
return (EXIT_SUCCESS);
}
/*
8
0 0
2 2
2 0
0 2
1 0
0 1
2 1
1 2
5
0 0
1 1
1 0
0.5 0
0.5 0.5
6
0 0
1 2
3 4
2 0
2 4
5 0
6
1 1
2 2
3 3
4 4
5 5
6 6
7
0 0
1 0
2 0
3 0
3 1
3 3
1 1
yes
no
no
no
yes
*/