HDU2823 The widest road

The widest road

给出两个点集,求出两个凸包后,求两个凸包的最近距离。需要特判两个凸包内含和相交各种乱七八糟的情况。

\(1 ≤ m,n ≤ 1000\)

题解

https://blog.csdn.net/clover_hxy/article/details/54022026

旋转卡壳求两凸包的最近距离

考虑如下的算法, 算法的输入是两个分别有\(m\)\(n\)个顺时针给定顶点的凸多边形P和Q。

  1. 计算P上y坐标值最小的顶点(称为 yminP )和Q上y坐标值最大的顶点(称为 ymaxQ)。

  2. 为多边形在 yminP 和 ymaxQ 处构造两条切线 LP 和 LQ 使得他们对应的多边形位于他们的右侧。

    此时 LP 和 LQ 拥有不同的方向, 并且 yminP 和 ymaxQ 成为了多边形间的一个对踵点对。

  3. 计算距离(yminP,ymaxQ) 并且将其维护为当前最小值。

  4. 顺时针同时旋转平行线直到其中一个与其所在的多边形的边重合。

  5. 如果只有一条线与边重合, 那么只需要计算“顶点-边”对踵点对和“顶点-顶点”对踵点对距离。 都将他们与当前最小值比较, 如果小于当前最小值则进行替换更新。如果两条切线都与边重合,那么情况就更加复杂了。如果边“交叠”,也就是可以构造一条与两条边都相交的公垂线(但不是在顶点处相交), 那么就计算“边-边”距离。 否则计算三个新的“顶点-顶点”对踵点对距离。 所有的这些距离都与当前最小值进行比较, 若小于当前最小值则更新替换。

  6. 重复执行步骤4和步骤5, 直到新的点对为(yminP,ymaxQ)。

  7. 输出最小距离。

时间复杂度\(O(n\log n)\)

struct point {float128 x,y;};

IN bool operator==(CO point&a,CO point&b){
	return a.x==b.x and a.y==b.y;
}
IN bool operator<(CO point&a,CO point&b){
	return a.x!=b.x?a.x0;
}
IN int quad(CO point&a){
	if(a.x>0 and a.y>=0) return 1;
	else if(a.x<=0 and a.y>0) return 2;
	else if(a.x<0 and a.y<=0) return 3;
	else return 4;
}
IN bool operator<(CO line&a,CO line&b){
	if(quad(a.y)!=quad(b.y)) return quad(a.y)0;
}
IN point intersect(CO line&a,CO line&b){
	return b.x+b.y*cross(b.x-a.x,a.y)/cross(a.y,b.y);
}

CO int N=2e3+10;
CO float128 inf=1e9;
point p[N],q[N],ch[N],it[N];
line ln[N];

void convex(point p[],int&n){
	if(n==1) return; // edit 1
	sort(p+1,p+n+1);
	int m=0;
	for(int i=1;i<=n;++i){
		for(;m>=2 and cross(p[i]-ch[m-1],ch[m]-ch[m-1])>=0;--m);
		ch[++m]=p[i];
	}
	int o=--m;
	for(int i=n;i>=1;--i){
		for(;m>=o+2 and cross(p[i]-ch[m-1],ch[m]-ch[m-1])>=0;--m);
		ch[++m]=p[i];
	}
	n=--m,copy(ch+1,ch+n+1,p+1),p[n+1]=p[1];
//	cerr<<"p="<0) return 0;
	return 1;
}
bool seg_poly_inter(point a,point b,point p[],int n){
	for(int i=1;i<=n;++i)
		if(seg_inter(a,b,p[i],p[i+1])) return 1;
	return 0;
}
bool halfplane(point p[],int n,point q[],int m){
	for(int i=1;i<=n;++i)
		ln[i].x=p[i],ln[i].y=p[i+1]-p[i];
	for(int i=1;i<=m;++i)
		ln[n+i].x=q[i],ln[n+i].y=q[i+1]-q[i];
	sort(ln+1,ln+n+m+1);
	int num=1;
	for(int i=2;i<=n+m;++i){
		if(cross(ln[i].y,ln[num].y)==0) continue;
		ln[++num]=ln[i];
	}
	int l=1,r=1;
	for(int i=2;i<=num;++i){
		for(;l=3;
}
float128 rotate(point p[],int n,point q[],int m){
	int x=1,y=1;
	for(int i=2;i<=n;++i)if(p[i].yq[y].y) y=i;
	float128 ans=inf;
	for(int i=1;i<=n;++i,x=x%n+1){
		for(;cross(p[x+1]-p[x],q[y+1]-q[y])>=0;y=y%m+1); // edit 2
//		cerr<<"now ("<m) swap(n,m),swap(p,q);
	float128 ans=inf;
	if(n==1){
		if(m==1) ans=len(p[1]-q[1]);
		else if(m==2) ans=dist_seg(p[1],q[1],q[2]);
		else{
			if(in_poly(p[1],q,m)) ans=0;
			else{
				for(int i=1;i<=m;++i)
					ans=min(ans,dist_seg(p[1],q[i],q[i+1]));
			}
		}
		printf("%.4lf\n",ans);
		return;
	}
	if(n==2){
		if(m==2){
			if(seg_inter(p[1],p[2],q[1],q[2])) ans=0;
			else{
				ans=min(ans,dist_seg(p[1],q[1],q[2]));
				ans=min(ans,dist_seg(p[2],q[1],q[2]));
				ans=min(ans,dist_seg(q[1],p[1],p[2]));
				ans=min(ans,dist_seg(q[2],p[1],p[2]));
			}
		}
		else{
			if(in_poly(p[1],q,m) or in_poly(p[2],q,m)) ans=0;
			else if(seg_poly_inter(p[1],p[2],q,m)) ans=0;
			else{
				for(int i=1;i<=m;++i){
					ans=min(ans,dist_seg(p[1],q[i],q[i+1]));
					ans=min(ans,dist_seg(p[2],q[i],q[i+1]));
					ans=min(ans,dist_seg(q[i],p[1],p[2]));
					ans=min(ans,dist_seg(q[i+1],p[1],p[2]));
				}
			}
		}
		printf("%.4lf\n",ans);
		return;
	}
	if(halfplane(p,n,q,m)) ans=0;
	else{
		ans=min(ans,rotate(p,n,q,m));
		ans=min(ans,rotate(q,m,p,n));
	}
	printf("%.4lf\n",ans);
}
int main(){
	for(int n,m;scanf("%d%d",&n,&m)!=EOF;) real_main(n,m);
	return 0;
}

你可能感兴趣的:(HDU2823 The widest road)