凸包问题的五种解法:https://blog.csdn.net/bone_ace/article/details/46239187
15学长的总结:https://blog.csdn.net/qq_34374664/article/details/70149223
附上自己写的代码加上理解吧:
给n个点,打印凸包上的点。
Graham扫描法:
时间复杂度:O(n㏒n)
思路:Graham扫描的思想和Jarris步进法类似,也是先找到凸包上的一个点,然后从那个点开始按逆时针方向逐个找凸包上的点,但它不是利用夹角。
步骤:
1、把所有点放在二维坐标系中,则纵坐标最小的点一定是凸包上的点,如图中的P0。
2、把所有点的坐标平移一下,使 P0 作为原点,如上图。
3、计算各个点相对于 P0 的幅角 α ,按从小到大的顺序对各个点排序。当 α 相同时,距离 P0 比较近的排在前面。例如上图得到的结果为 P1,P2,P3,P4,P5,P6,P7,P8。我们由几何知识可以知道,结果中第一个点 P1 和最后一个点 P8 一定是凸包上的点。
(以上是准备步骤,以下开始求凸包)
以上,我们已经知道了凸包上的第一个点 P0 和第二个点 P1,我们把它们放在栈里面。:
4、取出栈顶第一个点p1和第二个点p2,根据下一个点p,若p在直线p1p2的左边,则p1是凸包上的点,把p压到栈中,继续根据下一个点判断栈顶的点,若在右边,则栈顶的点不是凸包上的点,把栈顶的点pop出去,继续根据该点判断栈顶的点。
最后,栈中的元素就是凸包上的点了。
以下为用Graham扫描法动态求解的过程:
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
#define eps 1e-8
struct Point{
double x, y;
Point (double x_=0,double y_=0)
{
x=x_;y=y_;
}
Point operator + (const Point &xx)const
{
return Point(x+xx.x,y+xx.y);
}
Point operator - (const Point &xx)const
{
return Point(x-xx.x,y-xx.y);
}
}a[10010],b[10010];
double cross(Point xx,Point yy)
{
return (xx.x*yy.y-xx.y*yy.x);
}
double dis(Point xx,Point yy)
{
return sqrt((xx.x-yy.x)*(xx.x-yy.x) + (xx.y-yy.y)*(xx.y-yy.y));
}
int n,m;
bool cmp(Point xx,Point yy)
{
double O=cross(xx-a[1],yy-a[1]);// 利用叉积判断相对位置
if(O>0 || O==0&&dis(a[1],xx)<dis(a[1],yy)) return 1;
else return 0;
}
/* 这里也可以通过cos,判断角度的大小排序
bool cmp(Point xx,Point yy)
{
double O1=acos((xx.x-a[1].x) / sqrt((xx.x-a[1].x)*(xx.x-a[1].x) + (xx.y-a[1].y)*(xx.y-a[1].y)));
double O2=acos((yy.x-a[1].x) / sqrt((yy.x-a[1].x)*(yy.x-a[1].x) + (yy.y-a[1].y)*(yy.y-a[1].y)));
if(O1!=O2) return O1<O2;
else return dis(a[1],xx)<dis(a[1],yy);
}
*/
void solve()
{
for(int i=1;i<=min(2,n);i++)
b[++m]=a[i];
for(int i=3;i<=n;i++)
{
while(m>1 && cross(b[m]-b[m-1],a[i]-b[m-1])<=0)m--;
b[++m]=a[i];
}
double ans=0;
for(int i=1;i<=m;i++)
{
printf("%.3f %.3f\n",b[i].x,b[i].y);
}
}
int main()
{
while(~scanf("%d",&n))
{
int k=1;
m=0;
for(int i=1;i<=n;i++)
{
scanf("%lf%lf",&a[i].x,&a[i].y);
if(a[i].y<a[k].y|| (a[i].y==a[k].y&&a[i].x<a[k].x))
{
k=i;
}
}
swap(a[1].x,a[k].x);swap(a[1].y,a[k].y);
sort(a+2,a+1+n,cmp);
solve();
}
return 0;
}
分治法:
先找出最左和最右的两点,这两个肯定是凸包上的,根据点p1,pn把点集分为两部分,上和下,每一部分都求距离p1pn最远的点,这个可以根据叉积求个最大值即可,这样也就同样判断了是否在这一部分内,这样找到了pmax,然后在继续根据p1pmax和pmaxpn继续递归,下一部分也同样如此。
在计算距离最远时,直接利用叉积计算即可
算法代码:
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
#define eps 1e-8
struct Point{
double x, y;
Point (double x_=0,double y_=0)
{
x=x_;y=y_;
}
Point operator + (const Point &xx)const
{
return Point(x+xx.x,y+xx.y);
}
Point operator - (const Point &xx)const
{
return Point(x-xx.x,y-xx.y);
}
bool operator ==(const Point &xx)const
{
return fabs(x-xx.x)<eps&&fabs(y-xx.y)<eps;
}
}a[10010],b[10010];
double cross(Point xx,Point yy)
{
return (xx.x*yy.y-xx.y*yy.x);
}
double dis(Point xx,Point yy)
{
return sqrt((xx.x-yy.x)*(xx.x-yy.x) + (xx.y-yy.y)*(xx.y-yy.y));
}
bool cmp(Point xx,Point yy)
{
double O=cross(xx-b[1],yy-b[1]);
if(O>0 || O==0&&dis(b[1],xx)<dis(b[1],yy)) return 1;
else return 0;
}
int n,m;
void getresult(Point c[10010],int num,double x1,double y1,double x2,double y2)
{
if(num<=1) return;
Point d[10010];
int cnt=0;
Point now=Point(x2,y2)-Point(x1,y1);
double maxx=cross(now,c[1]-Point(x1,y1));
int id=1;
d[++cnt]=c[1];
double tmp;
for(int i=2;i<=num;i++)
{
tmp=cross(now,c[i]-Point(x1,y1));
if(tmp>=0) // 在这一部分的加到临时数组中
d[++cnt]=c[i];
if(tmp>maxx)
{
maxx=tmp;
id=i;
}
}
// cout<<c[id].x<<" "<<c[id].y<<endl;
if(maxx<=0) //
{
for(int i=1;i<=num;i++)
{
tmp=cross(now,c[i]-Point(x1,y1));// 一条直线的上的点都是凸包上的
if(tmp==0&&!(c[i]==Point(x1,y1))&&!(c[i]==Point(x2,y2)))
b[++m]=c[i];
}
return;
}
else
b[++m]=c[id];// 距离最大的也是凸包上的
getresult(d,cnt,x1,y1,c[id].x,c[id].y);
getresult(d,cnt,c[id].x,c[id].y,x2,y2);
}
int main()
{
while(~scanf("%d",&n))
{
int k=1;
m=0;
for(int i=1;i<=n;i++)
scanf("%lf%lf",&a[i].x,&a[i].y);
double x1,x2,y1,y2;
x1=x2=a[1].x;
y1=y2=a[1].y;
for(int i=2;i<=n;i++)
{
if(a[i].x<x1)
{
x1=a[i].x;
y1=a[i].y;
}
if(a[i].x>x2)
{
x2=a[i].x;
y2=a[i].y;
}
}
b[++m].x=x1;b[m].y=y1;
b[++m].x=x2;b[m].y=y2;
getresult(a,n,x1,y1,x2,y2);
getresult(a,n,x2,y2,x1,y1);
double ans=0;
sort(b+2,b+1+m,cmp);
for(int i=1;i<=m;i++) cout<<b[i].x<<" "<<b[i].y<<endl;
}
return 0;
}