poj 1228 凸包(能否恢复农场形状)

题意:一个由钉子加绳子围成的凸多边形农场。现 在绳子和部分钉子缺失,问能否通过剩余的钉子确定原农场的样子。

思路:做凸包。当围成的凸包每条边上都有至少3个钉子(含端点),则可确定原农场样子。因为如果只有两个钉子,那么可能存在一个消失的钉子位于这条边的外面,使得所围的农场变大,且凸包性质没变。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define N 1005
int T,n,top;
struct point {
    int x,y;
}p[N];
int stack[N];
int multi(point a,point b,point c){
    return (b.x-a.x)*(c.y-a.y) - (b.y-a.y)*(c.x-a.x);
}
int dist(point a,point b){
    return (a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y);
}
int cmp(point a,point b){
    int j = multi(p[0],a,b);
    if(j == 0)
        return dist(p[0],a) < dist(p[0],b);
    return j>0;
}
int test(){
    int i,j;
    if(top == 1)//最后凸包成为一条直线
        return 0;
    for(j = 0;j<top;j++){
        if(stack[j] == stack[j+1]-1)
            return 0;//表示凸包中两点之间没有点,那么这条直线未必是原来农场中存在的
        for(i = stack[j]+1;i<=stack[j+1]-1 && multi(p[0], p[i], p[stack[top]])!=0;i++)
            if(multi(p[stack[j]], p[stack[j+1]], p[i]) != 0)//有在凸包之内的点的情况
                return 0;
    }
    if(i==n-1)//原点到逆时针最后一个点之间没有点的情况
        return 0;
    for(;i<n-1;i++)
        if(multi(p[0], p[i], p[stack[top]]) != 0)
            return 0;
    return 1;
}
int main(){
    scanf("%d",&T);
    while(T--){
        int i,j,start = 1;
        top = -1;
        scanf("%d",&n);
        for(i = 1;i<=n;i++){
            scanf("%d %d",&p[i].x,&p[i].y);
            if(p[i].y<p[start].y || (p[i].y==p[start].y && p[i].x<p[start].x))
                start = i;
        }
        if(n==1){
            printf("NO\n");
            continue;
        }
        p[0] = p[start];
        for(i = start+1;i<=n;i++)
            p[i-1] = p[i];
        sort(p,p+n,cmp);
        stack[++top] = 0;
        stack[++top] = 1;
        for(i = 2;i<n;i++){
            while(top && multi(p[stack[top-1]],p[stack[top]],p[i])<=0)
                top--;
            stack[++top] = i;
        }
        if(test())
            printf("YES\n");
        else
            printf("NO\n");
    }
}


你可能感兴趣的:(poj 1228 凸包(能否恢复农场形状))