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
",stackk[i].x,stackk[i].y);
39         }
40     }
41     return 0;
42 }
原文地址:https://www.cnblogs.com/acgoto/p/9552160.html