凸包问题--旋转卡壳

前情提要:

1978年,M.I.Shamos在论文《Computational Ceometry》中介绍了一种寻找凸多边形直径的线性算法。

Shamos的算法就像绕着多边形旋转一对卡壳,因此便有了术语——旋转卡壳。旋转卡壳是一种高效的算法。被广泛运用在解决一些与凸包相关的问题。旋转卡壳充分利用了凸包的凸性,通常可以在线性时间内内解决问题。

有关概念:

1.支撑线:如果一条直线L,通过凸多边形P的一个顶点,且多边形在这条直线的一侧,称L是P的支撑线。

2.对踵(zhong,三声)点:如果过凸包上的两个点可以画一对平行直线,使凸包上所有点都夹在两条平行线之间||落在平行线上,那么这两个点称为一对对踵点。两条平行的支撑线所过的两点就是一对对踵点。可以证明,一个凸n边形的对踵点最多只有3n/2(对上取整)对。

3.凸多边形的直径:将一个凸多边形上任意两点间的距离的最大值定义为多边形的直径。确定这个直径的点对数可能多于一对。事实上,对于拥有n个顶点的多边形,就可能有n对“直径点对”存在。

存在一个凸多边形直径定理:凸多边形P的直径=P的所有平行支撑线之间距离的最大值,因此,凸多边形的直径可以在对踵点对之间寻找。

旋转卡壳的步骤:

Step1:计算多边形y方向上的端点,称之为ymin和ymax。

Step2:通过ymin和ymax构造两条水平切线,由于他们已经是一对对踵点,计算他们之间的距离并维护一个当前最大值。

Step3:同时旋转两条直线到其中一条与多边形的一条边重合。

Step4:一个新的对踵点对此时产生,计算新的距离,并和当前最大值进行比较,若大于当前最大值。则更新。

Step5:重复Step3和Step4的过程直到再次产生新的对踵点对。

Step6:输出最大直径的对踵点对。

可以看出,如果单对一个凸包进行旋转卡壳,其时间复杂度为O(n),然而,由于一般在此之前要计算凸包,其时间复杂度为O(nlogn),因此,求一组点的最大距离的时间复杂度一般为O(nlogn)。

当然,如果按照上面所说的实现的话,感觉好麻烦,于是我们可以换成三角形面积,然后一个一个的替换。可以产生一样的效果。

例如:Step2中每次查找一对对踵点,我们就可以转换成求三角形的面积:如图,红色和蓝色三角形。最后比较的时候,比较最后的两条线即可。

下面给出一组图来形象的解释算法步骤:

第一次比较,即线段AB和三角形ABC之间比面积,得到:符合,q=(q+1)%n;q=2;

第二次比较,即三角形ABC和三角形ACD之间比面积,得到:符合,q=(q+1)%n;q=3;

第三次比较,即三角形ABD和三角形ABE之间,进过我仔细的比较,发现ABD比较大,因此结束while循环,ans更新为max(AD,BE),再次经过我仔细的比较,发现BE较长,因此ans更新为Distance(D,E);

i++,继续循环,直到遍历完所有的顶点。

算法的思路比较简单,实现也不难,只用几行代码即可。

Code:


struct Point{
	double x,y,t,d;
	Point(double _x=0,double _y=0,double _t=0,double _d=0){
		x=_x;y=_y;t=_t;d=_d;
	}
	friend Point operator + (const Point &a,const Point &b){
		return Point(a.x+b.x,a.y+b.y);
	}
	friend Point operator - (const Point &a,const Point &b){
		return Point(a.x-b.x,a.y-b.y);
	}
	friend double operator ^ (Point a,Point b){//??????? 
		return a.x*b.y-a.y*b.x;
	}
};
double Dis(Point a,Point b){
	return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
double RotateCalipers(Point *ch,int n){//传入点集和集合元素个数 
	double ans=-INF32;int q=1;
	ch[n]=ch[0];
	for(int i=0;i<n;++i){
		while(((ch[q]-ch[i+1])^(ch[i]-ch[i+1]))<((ch[q+1]-ch[i+1])^(ch[i]-ch[i+1]))) q=(q+1)%n;
		ans=max(ans,max(Dis(ch[q],ch[i]),Dis(ch[q+1],ch[i+1])));
	}return ans;
}

然后是一些OJ 的测试来验证这个板子了:

POJ-2187-Beauty Contest(凸包+旋转卡壳板子):

ACCode:https://blog.csdn.net/qq_40482358/article/details/88541992

猜你喜欢

转载自blog.csdn.net/qq_40482358/article/details/88087039