Grandpa's Estate POJ 1228 (凸包)

题目地址:http://poj.org/problem?id=1228
凸包丢失了若干个点,给你剩余的点,求这些剩余的点能不能构成一个唯一的凸包,即剩余点加上任意点(剩余点还在凸包边上),只能构成一个凸包。
在剩余点中,一遍至少三个点才能保持唯一性,两点的话可以在两点之间的外面加一点,三点加一点的话,原先三点中的中间的点会失效。
一种错误的写法
建两个凸包,一个允许多点共线,一个不允许,看这两个凸包是否存在边相等,


#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<iostream>
using namespace std;
const int MAXN=50098;
const double PI=acos(-1.0);

struct point
{
    int x,y;
};
point list[MAXN];
int stack1[MAXN],stack2[MAXN],top1,top2;

int cross(point p0,point p1,point p2) //计算叉积  p0p1 X p0p2
{
    return (p1.x-p0.x)*(p2.y-p0.y)-(p1.y-p0.y)*(p2.x-p0.x);
}
double dis(point p1,point p2)  //计算 p1p2的 距离
{
    return sqrt((double)(p2.x-p1.x)*(p2.x-p1.x)+(p2.y-p1.y)*(p2.y-p1.y));
}
bool cmp(point p1,point p2) //极角排序函数 , 角度相同则距离小的在前面
{
    int tmp=cross(list[0],p1,p2);
    if(tmp>0) return true;
    else if(tmp==0&&dis(list[0],p1)<dis(list[0],p2)) return true;
    else return false;
}
void init(int n) //输入,并把  最左下方的点放在 list[0]  。并且进行极角排序
{
    int i,k;
    point p0;
    scanf("%d%d",&list[0].x,&list[0].y);
    p0.x=list[0].x;
    p0.y=list[0].y;
    k=0;
    for(i=1;i<n;i++)
    {
        scanf("%d%d",&list[i].x,&list[i].y);
        if( (p0.y>list[i].y) || ((p0.y==list[i].y)&&(p0.x>list[i].x)) )
        {
            p0.x=list[i].x;
            p0.y=list[i].y;
            k=i;
        }
    }
    list[k]=list[0];
    list[0]=p0;
    sort(list+1,list+n,cmp);
}


void graham1(int n)
{
    int i;
    if(n==1) {top1=0;stack1[0]=0;}
    if(n==2)
    {
        top1=1;
        stack1[0]=0;
        stack1[1]=1;
    }
    if(n>2)
    {
        for(i=0;i<=1;i++) stack1[i]=i;
        top1=1;

        for(i=2;i<n;i++)
        {
            while(top1>0&&cross(list[stack1[top1-1]],list[stack1[top1]],list[i])<=0) top1--;
            top1++;
            stack1[top1]=i;
        }
    }
}
void graham2(int n)
{
    int i;
    if(n==1) {top2=0;stack2[0]=0;}
    if(n==2)
    {
        top2=1;
        stack2[0]=0;
        stack2[1]=1;
    }
    if(n>2)
    {
        for(i=0;i<=1;i++) stack2[i]=i;
        top2=1;

        for(i=2;i<n;i++)
        {
            while(top2>0&&cross(list[stack2[top2-1]],list[stack2[top2]],list[i])<0) top2--;
            top2++;
            stack2[top2]=i;
        }
    }
}
bool judge()
{
    int j=0;
    top1++;
    top2++;
    for(int i=0;i<top1;i++)
    {
        while(stack1[i]!=stack2[j])
            j=(j+1)%top2;
        if(stack1[i]==stack2[j]&&stack1[(i+1)%top1]==stack2[(j+1)%top2])
            return  false;
    }
    return  true;
}
int main()
{

   int t,n;
   scanf("%d",&t);
   while(t--)
   {
        scanf("%d",&n);
        memset(stack1,0,sizeof(stack1));
           init(n);
            graham1(n);
            graham2(n);
        if(judge())
            printf("YES\n");
        else
            printf("NO\n");
   }


    return 0;
}

一种AC的:
建一个允许多点共线的凸包,求他与相邻两点或其后两点是否共线

#include<stdio.h>
#include<math.h>
#include<algorithm>
#include<iostream>
#include<string.h>
using namespace std;
const int MAXN=1005;
const double PI=acos(-1.0);

struct point
{
    int x,y;
};
point list[MAXN];
int stack[MAXN],top;

int cross(point p0,point p1,point p2) //计算叉积  p0p1 X p0p2
{
    return (p1.x-p0.x)*(p2.y-p0.y)-(p1.y-p0.y)*(p2.x-p0.x);
}
double dis(point p1,point p2)  //计算 p1p2的 距离
{
    return sqrt((double)(p2.x-p1.x)*(p2.x-p1.x)+(p2.y-p1.y)*(p2.y-p1.y));
}
bool cmp(point p1,point p2) //极角排序函数 , 角度相同则距离小的在前面
{
    int tmp=cross(list[0],p1,p2);
    if(tmp>0) return true;
    else if(tmp==0&&dis(list[0],p1)<dis(list[0],p2)) return true;
    else return false;
}
void init(int n) //输入,并把  最左下方的点放在 list[0]  。并且进行极角排序
{
    int i,k;
    point p0;
    scanf("%d%d",&list[0].x,&list[0].y);
    p0.x=list[0].x;
    p0.y=list[0].y;
    k=0;
    for(i=1;i<n;i++)
    {
        scanf("%d%d",&list[i].x,&list[i].y);
        if( (p0.y>list[i].y) || ((p0.y==list[i].y)&&(p0.x>list[i].x)) )
        {
            p0.x=list[i].x;
            p0.y=list[i].y;
            k=i;
        }
    }
    list[k]=list[0];
    list[0]=p0;
    sort(list+1,list+n,cmp);
}

void graham(int n)
{
    int i;
    if(n==1) {top=0;stack[0]=0;}
    if(n==2)
    {
        top=1;
        stack[0]=0;
        stack[1]=1;
    }
    if(n>2)
    {
        for(i=0;i<=1;i++) stack[i]=i;
        top=1;

        for(i=2;i<n;i++)
        {
            while(top>0&&cross(list[stack[top-1]],list[stack[top]],list[i])<0) top--;
            top++;
            stack[top]=i;
        }
    }
}
bool judge()
{
  //stack[++top]=stack[0];
  //stack[++top]=stack[1];
  //printf("%d\n",top);
 /* for(int i=0;i<=top;i++)
  {
      printf("%d  %d *",list[stack[i]].x,list[stack[i]].y);
  }
  printf("\n");*/
    for(int i=0;i<top;i++)
    {
       if(i==0)
        {
              if(cross(list[stack[i]],list[stack[i+1]],list[stack[i+2]])!=0)
              {
                  //printf("%d**\n",i);
                 // printf("%d %d %d %d %d %d\n",
///list[stack[i]].x,list[stack[i]].y,list[stack[i+1]].x,list[stack[i+1]].y,list[stack[i+2]].x,list[stack[i+2]].y);
                     return false;

              }

        }

        else
        if(cross(list[stack[i]],list[stack[i+1]],list[stack[i+2]])!=0&&cross(list[stack[i-1]],list[stack[i]],list[stack[i+1]])!=0)
                        return false;
    }
    return true;
}
int main()
{

   int t,n;
   scanf("%d",&t);
   while(t--)
   {
        scanf("%d",&n);
        memset(stack,0,sizeof(stack));
           init(n);
            graham(n);
            if(n<6)
            {
                printf("NO\n");
                continue;
            }
            else
            {
                if(judge())
            printf("YES\n");
                  else
            printf("NO\n");
            }

   }
    return 0;
}

发现一点数据bug
1
7
0 0
1 0
2 0
2 1
2 2
1 2
0 2
YES
按照题意是NO AC代码确实YES

真正AC的代码
1将凸包求全了
2判断凸包最后两个点的时候改进了

#include<stdio.h>
#include<math.h>
#include<algorithm>
#include<iostream>
#include<string.h>
using namespace std;
const int MAXN=1005;
const double PI=acos(-1.0);
int flag;
struct point
{
    int x,y;
};
point list[MAXN];
int stack[MAXN],top;

int cross(point p0,point p1,point p2) //计算叉积  p0p1 X p0p2
{
    return (p1.x-p0.x)*(p2.y-p0.y)-(p1.y-p0.y)*(p2.x-p0.x);
}
double dis(point p1,point p2)  //计算 p1p2的 距离
{
    return sqrt((double)(p2.x-p1.x)*(p2.x-p1.x)+(p2.y-p1.y)*(p2.y-p1.y));
}
bool cmp(point p1,point p2) //极角排序函数 , 角度相同则距离小的在前面
{
    int tmp=cross(list[0],p1,p2);
    if(tmp>0) return true;
    else if(tmp==0&&dis(list[0],p1)<dis(list[0],p2)) return true;
    else return false;
}
void init(int n) //输入,并把  最左下方的点放在 list[0]  。并且进行极角排序
{
    int i,k;
    point p0;
    scanf("%d%d",&list[0].x,&list[0].y);
    p0.x=list[0].x;
    p0.y=list[0].y;
    k=0;
    for(i=1;i<n;i++)
    {
        scanf("%d%d",&list[i].x,&list[i].y);
        if( (p0.y>list[i].y) || ((p0.y==list[i].y)&&(p0.x>list[i].x)) )
        {
            p0.x=list[i].x;
            p0.y=list[i].y;
            k=i;
        }
    }
    list[k]=list[0];
    list[0]=p0;
    sort(list+1,list+n,cmp);
}

void graham(int n)
{
    int i;
    if(n==1) {top=0;stack[0]=0;}
    if(n==2)
    {
        top=1;
        stack[0]=0;
        stack[1]=1;
    }
    if(n>2)
    {
        for(i=0;i<=1;i++) stack[i]=i;
        top=1;

        for(i=2;i<n;i++)
        {
            while(top>0&&cross(list[stack[top-1]],list[stack[top]],list[i])<0) top--;
            top++;
            stack[top]=i;
        }
    }
   // printf("&&\n");
    for(int i=stack[top]-1;i>0;i--)
    {
        if(cross(list[0],list[stack[top]],list[i])==0)
         {

                        stack[++top]=i;
        }
    }
    //printf("%d\n",top);
}
bool judge()
{
  stack[++top]=stack[0];
  stack[++top]=stack[1];
  /*printf("%d\n",top);
  for(int i=0;i<=top;i++)
  {
      printf("%d  %d *",list[stack[i]].x,list[stack[i]].y);
  }
  printf("\n");*/
    for(int i=0;i<top-1;i++)
    {
       if(i==0)
        {
              if(cross(list[stack[i]],list[stack[i+1]],list[stack[i+2]])!=0)
                     return false;
        }
        else
        if(cross(list[stack[i]],list[stack[i+1]],list[stack[i+2]])!=0&&cross(list[stack[i-1]],list[stack[i]],list[stack[i+1]])!=0)
                        return false;
    }
    return true;
}
int main()
{

   int t,n;
   scanf("%d",&t);
   while(t--)
   {
        scanf("%d",&n);
        memset(stack,0,sizeof(stack));
           init(n);
            graham(n);
            if(n<6)
            {
                printf("NO\n");
                continue;
            }
            else
            {
                if(judge())
            printf("YES\n");
                  else
            printf("NO\n");
            }

   }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值