题意:一个由钉子加绳子围成的凸多边形农场。现 在绳子和部分钉子缺失,问能否通过剩余的钉子确定原农场的样子。
思路:做凸包。当围成的凸包每条边上都有至少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"); } }