凸包问题-求在一群散乱的点中,找出一定的点通过连线可以将所有的点给囊括其中——也就是求能包括这所有点的最大周长。
算法步骤:
第一步:首先找出y值最小的点P0,因为可以确定的是如果一个点的y值最小那么他一定在凸包上。
第二步:以这个点P0未基准,给其他的点进行排序,(按照极值从小到大排序)
第三步:将P0,P1,P2三个点纳入凸包里,开始扫描,如果p0-p1 p1-p2的叉积大于0 表示 p0 p1 p2之间的夹角小于180度,那么p1就是凸包上的点,否则,p1就不是凸包上的点,然后继续往下扫描。
代码如下:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
int n; // 表示点的个数
struct node {
int x,y;
} vex[1000]; //点
node invex[1000];
int xx,yy;
int cmp(node a,node b) {
return a.y<b.y;
} //第一次排序
int cmp1(node a,node b) {
if(atan2(a.y-yy,a.x-xx)!=atan2(b.y-yy,b.x-xx))
return (atan2(a.y-yy,a.x-xx))<(atan2(b.y-yy,b.x-xx));
return a.x<b.x;
} //第二次排序
int cross(node a,node b,node c) { //计算 p0 p1 p2 之间的叉积
return (b.x-a.x)*(c.y-a.y)-(c.x-a.x)*(b.y-a.y);
}
double dis(node a, node b) { //计算距离
return sqrt((a.x-b.x)*(a.x-b.x)*1.0+(a.y-b.y)*(a.y-b.y));
}
int main() {
scanf("%d",&n);
for(int i=0; i<n; i++) {
scanf("%d%d",&vex[i].x,&vex[i].y);
}
sort(vex,vex+n,cmp); //第一次排序找出y值最小的点
invex[0]=vex[0]; //进入凸包的点的集合
xx = invex[0].x;
yy = invex[0].y;
sort(vex+1,vex+n,cmp1); //根据p0来进行第二次排序,以极值排序
invex[1]=vex[1]; // p1 进入
int top = 1;
for(int i=2; i<n; i++) {
while(top>=1 && cross(invex[top-1],invex[top],vex[i])<0) {//如果叉积小于0则不能进入
top--;
}
invex[++top]=vex[i];
}
double s = 0;
for(int i=1; i<n; i++) { //计算凸包的周长
s+=dis(invex[i-1],invex[i]);
}
s+=dis(invex[0],invex[top]); //最后invex[0] invex[top] 整个凸包连在一起
printf("%.2lf\n",s);
return 0;
}