nyoj #78 圈水池(打印凸包顶点)
描述:
有一个牧场,牧场上有很多个供水装置,现在牧场的主人想要用篱笆把这些供水装置圈起来,以防止不是自己的牲畜来喝水,各个水池都标有各自的坐标,现在要你写一个程序利用最短的篱笆将这些供水装置圈起来!(篱笆足够多,并且长度可变)
输入:
第一行输入的是N,代表用N组测试数据(1<=N<=10)
第二行输入的是m,代表本组测试数据共有m个供水装置(3<=m<=100)
接下来m行代表的是各个供水装置的横纵坐标
输出:
输出各个篱笆经过各个供水装置的坐标点,并且按照x轴坐标值从小到大输出,如果x轴坐标值相同,再安照y轴坐标值从小到大输出
样例输入:
1 4 0 0 1 1 2 3 3 0
样例输出:
0 0 2 3 3 0
解题思路:题意说得很清楚,按规则输出凸包上所有的顶点坐标。这里用Andrew算法,注意必须构造完整的凸包,即构造凸包上侧时要将起点包括进去(必须i>=0不能i>0),最后舍去一个多余的起点再排序输出即可。
AC代码(0ms):
1 #include<iostream> 2 #include<string.h> 3 #include<algorithm> 4 #include<cstdio> 5 #include<cmath> 6 using namespace std; 7 const int maxn=105; 8 struct node{int x,y;}vex[maxn],stackk[maxn]; 9 bool cmp(node a,node b){//找基点 10 return ((a.y<b.y)||(a.y==b.y&&a.x<b.x)); 11 } 12 bool cmp1(node a,node b){ 13 return ((a.x<b.x)||(a.x==b.x&&a.y<b.y)); 14 } 15 int cross(node p0,node p1,node p2){ 16 return (p1.x-p0.x)*(p2.y-p0.y)-(p2.x-p0.x)*(p1.y-p0.y); 17 } 18 int main(){ 19 int t,n; 20 while(~scanf("%d",&t)){ 21 while(t--){ 22 scanf("%d",&n); 23 for(int i=0;i<n;++i) 24 scanf("%d%d",&vex[i].x,&vex[i].y); 25 memset(stackk,0,sizeof(stackk)); 26 sort(vex,vex+n,cmp); 27 int top=-1; 28 for(int i=0;i<n;++i){//构造凸包下侧 29 while(top>0&&cross(stackk[top-1],stackk[top],vex[i])<=0)top--; 30 stackk[++top]=vex[i]; 31 } 32 for(int i=n-2,k=top;i>=0;--i){//构造凸包上侧,要构造完整的凸包(i>=0),最后一个坐标点即起点舍去即可 33 while(top>k&&cross(stackk[top-1],stackk[top],vex[i])<=0)top--; 34 stackk[++top]=vex[i]; 35 } 36 sort(stackk,stackk+top,cmp1);//舍弃最后一个多余的起点坐标 37 for(int i=0;i<top;++i) 38 printf("%d %d\n",stackk[i].x,stackk[i].y); 39 } 40 } 41 return 0; 42 }