pku1228 Grandpa's Estate

给你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
*/

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值