圈水池
时间限制:
3000 ms | 内存限制:
65535 KB
难度:
4
- 描述
- 有一个牧场,牧场上有很多个供水装置,现在牧场的主人想要用篱笆把这些供水装置圈起来,以防止不是自己的牲畜来喝水,各个水池都标有各自的坐标,现在要你写一个程序利用最短的篱笆将这些供水装置圈起来!(篱笆足够多,并且长度可变)
- 输入
-
第一行输入的是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算法,Andrew是基于水平序的一种算法(且不能有重复点)。
我们要做的第一步是将给定点按照x从小到大排序,若x相同则按照y从小到大排序。设排序好之后的点序列为p1,p2,p3,.... ,pn我们需要先将最左边的两个点A,B加入 到凸包集合中,接着对下一个点C进行判断,判断向量AC是否在向量AB的下方,如果是,那么将B点从凸包集合中删除,加入新的点,如图1,C符合条件,所以我们将B点删除。
接着再对下一个点进行判断,将它与凸包集合里的后两个点进行比较,还是判断新形成的向量是否在下方,是的话则删除凸包集合的最后一个点,接着继续将新的点加入到集合(不管凸包集合的最后一个点是否被删除,都加入新的点),如图2,新的点不符合条件,所以我们凸包集合中的最后一个点不用删除,直接将新点加入到凸包集合中。
那么我们循环n次之后就可以得到凸包的下半部分 (图5)。于是我们再反着求一次,就可以得到凸包的上部分了,凸包就完成了。。
小tips:
这里计算两个向量Vector1和Vector2的位置关系我们可以利用两个向量的叉积,如果叉积大于0,则说明Vector1在Vector2的上方
#include<stdio.h>
#include<math.h>
#include<stdlib.h>
#include<algorithm>
using namespace std;
struct Point{
double x,y;
Point(double x=0,double y=0):x(x),y(y){
}
}p[110],ch[110];
typedef Point Vector;//定义Point的别名
//对自定义的结构体进行运算符重载
Vector operator - (Vector A,Vector B){
return Vector(A.x-B.x,A.y-B.y);
}
Vector operator * (Vector A,double p){
return Vector(A.x*p,A.y*p);
}
bool operator < (const Point &A,const Point &B){
return A.x<B.x||(A.x==B.x&&A.y<B.y);//先比较x,如果x相同接着比较y
}
//计算叉积
double Cross(Vector A,Vector B){
return A.x*B.y-A.y*B.x;
}
void Andrew(Vector *p,int n,Vector *ch){
sort(p,p+n);//对点从小到大排序
//凸包下部分
int m=0;
for(int i=0;i<n;i++)
{
while(m>1&&Cross(ch[m-1]-ch[m-2],p[i]-ch[m-2])<=0) m--;//这里的m>1是确保凸包集合中至少存在一个向量
ch[m++]=p[i];
}
//凸包上部分
int k=m;
for(int i=n-2;i>0;i--)
{
while(m>k&&Cross(ch[m-1]-ch[m-2],p[i]-ch[m-2])<=0) m--;
ch[m++]=p[i];
}
sort(ch,ch+m);
for(int i=0;i<m;i++)
{
printf("%.0f %.0f\n",ch[i].x,ch[i].y);
}
}
int main()
{
int t;
int n;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
for(int i=0;i<n;i++)
{
int a,b;
scanf("%d%d",&a,&b);
p[i].x=a;
p[i].y=b;
}
Andrew(p,n,ch);
}
return 0;
}