凸包学习

凸包问题的五种解法: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;
}

猜你喜欢

转载自blog.csdn.net/mmk27_word/article/details/89469777