poj3608(旋轉卡殼求兩凸包間的最短距離)

/*
translation:
	求兩個凸包間的最小距離?
solution:
	旋轉卡殼法。
note:
	* 網上給出的資料都差不多,具體如下:
	1. 計算凸包P在y軸方向上的最小值記爲yminP,和凸包Q在y軸方向上的最大值記爲ymaxQ。
	2. 建立兩條緊貼着yminP, ymaxQ的兩條水平的直線LP, LQ。要求他們指向不同的方向。這時候他們就形成了一對anti-podal pair。
	3. 計算(yminP,ymaxQ)的距離,並記爲minimum.
	4. 將兩條直線順時針旋轉,直到其中一條遇到凸包的一條邊停止。
	5. 只要有一條直線遇到了一條邊,我們就需要計算新的vertex-vertex anti-podal之間的距離,並同minimum比較,並更新。如果兩條直線都分同     一     條     邊重合,那麼情況就複雜一些了。如果這兩條邊”重合“,也就是說我們可以畫一條垂直於這兩條直線的直線並且和凸包上的兩個邊都相交(不包括在頂點相交),那麼我們就需要計算兩條直線的距離了。否則的話我們只要計算3個新的頂點到頂點之間的距離。所有的具體都要同minimum比較,並更新minimum 的值。
	6. 重複步驟4,5,直到兩條直線又回到起始位置爲止。
	7. 輸出最小的距離。
	
	# 之所以在最後旋轉卡殼求2次是因爲旋轉卡殼要求兩個直線都已經回到原點後才能結束。但是由於具體代碼實現時,結束時不一定保證兩條
	  直線都已經回到原點(因爲一個凸包的點數可能較多)。所以可以將兩個凸包互換後再處理一次,就能保證正確性。
	# 題目所給的點都是按照時針順序排好的,如果不是的話還需要對其進行排序。
*/
#include <iostream>
#include <cstdio>
#include <vector>
#include <cmath>
#include <algorithm>

using namespace std;
const int maxn = 50000 + 5;
const double INF = 0x3f3f3f3f * 1.0;

struct Point
{
    double x, y;
    Point(){}
    Point(double x_, double y_):x(x_),y(y_){}
} p1[maxn], p2[maxn], t1[maxn], t2[maxn];
typedef Point Vector;
int n, m;

Vector operator + (Vector a, Vector b)  { return Vector(a.x + b.x, a.y + b.y); }
Vector operator - (Point a, Point b)    { return Point(a.x - b.x, a.y - b.y); }
Vector operator * (Vector a, double p)  { return Vector(a.x * p, a.y * p); }
Vector operator / (Vector a, double p)  { return Vector(a.x / p, a.y / p); }

bool operator < (const Point& a, const Point& b)
{
    return a.x < b.x || (a.x == b.x && a.y < b.y);
}

const double eps = 1e-10;
int dcmp(double x)
{
    if(fabs(x) < eps)    return 0;
    else                return x < 0 ? -1 : 1;
}

bool operator == (const Point& a, const Point& b)
{
    return dcmp(a.x - b.x) == 0 && dcmp(a.y - b.y) == 0;
}

double dot(Vector a, Vector b)      { return a.x * b.x + a.y * b.y; }
double length(Vector a)             { return sqrt(dot(a, a)); }
double angle(Vector a, Vector b)    { return acos(dot(a, b) / length(a) / length(b)); }
double angle(Vector v)              { return atan2(v.y, v.x); }
double cross(Vector a, Vector b)    { return a.x * b.y - b.x * a.y; }
double dist(Point p1,Point p2)      { return sqrt((p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y)); }
double area(Point a, Point b, Point c) { return cross(b - a, c - a); }

double distanceToSegment(Point p, Point a, Point b)
{
	if(a.x == b.x && a.y == b.y)	return length(p - a);
	Vector v1 = b - a, v2 = p - a, v3 = p - b;

	if(dcmp(dot(v1, v2)) < 0)	return length(v2);
	else if(dcmp(dot(v1, v3)) > 0)	return length(v3);
	else return fabs(cross(v1, v2)) / length(v1);
}

double lineDistance(Point a1, Point b1, Point a2, Point b2)
{
	double d1 = min(distanceToSegment(a1, a2, b2), distanceToSegment(b1, a2, b2));
	double d2 = min(distanceToSegment(a2, a1, b1), distanceToSegment(b2, a1, b1));
	return min(d1, d2);
}

double rotatingCalipers(Point* ch1, Point* ch2, int n, int m)
{
	int maxy = 0, miny = 0;
	for(int i = 0; i < n; i++)	if(ch1[i].y > ch1[maxy].y)	maxy = i;
	for(int i = 0; i < m; i++)	if(ch2[i].y < ch2[miny].y)	miny = i;

	ch1[n] = ch1[0];
	ch2[m] = ch2[0];

	double ans = INF;
	for(int i = 0; i < n; i++) {
		double arg;
		Vector v = ch1[maxy + 1] - ch1[maxy];
		while((arg = cross(v, ch2[miny + 1] - ch1[maxy]) - cross(v, ch2[miny] - ch1[maxy])) > eps)
			miny = (miny + 1) % m;
		ans = min(ans, lineDistance(ch1[maxy], ch1[maxy + 1], ch2[miny], ch2[miny + 1]));
		maxy = (maxy + 1) % n;
	}
	return ans;
}

int main()
{
	//freopen("in.txt", "r", stdin);
    while(~scanf("%d%d", &n, &m) && n + m) {
		for(int i = 0; i < n; i++)
			scanf("%lf%lf", &p1[i].x, &p1[i].y);
		for(int j = 0; j < m; j++)
			scanf("%lf%lf", &p2[j].x, &p2[j].y);


		printf("%.5lf\n", min(rotatingCalipers(p1, p2, n, m), rotatingCalipers(p2, p1, m, n)));
    }
    return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章