poj 3608 旋转卡壳求不相交凸包最近距离;
题目链接:http://poj.org/problem?id=3608
#include<cstdio> #include<cstring> #include<cmath> #include<iostream> #include<algorithm> #include<queue> using namespace std; const int maxn = 10500; const int maxe = 100000; const int INF = 0x3f3f3f; const double eps = 1e-8; const double PI = acos(-1.0); struct Point{ double x,y; Point(double x=0, double y=0) : x(x),y(y){ } //构造函数 }; typedef Point Vector; Vector operator + (Vector A , Vector B){return Vector(A.x+B.x,A.y+B.y);} Vector operator - (Vector A , Vector B){return Vector(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); } 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; } ///向量(x,y)的极角用atan2(y,x); 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 Cross(Vector A, Vector B) { return A.x*B.y - A.y * B.x; } Vector Rotate(Vector A, double rad) { return Vector(A.x*cos(rad)-A.y*sin(rad),A.x*sin(rad)+A.y*cos(rad)); } ///求点P到线段AB的距离,先看Q点在线段外还是内;利用点积就可以, double DistanceToSegment(Point P,Point A,Point B){ if(A == B) 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); } ///求线段AB与线段CD的距离; double DistanceBetweenSegment(Point A,Point B,Point C,Point D){ return min(min(DistanceToSegment(A,C,D),DistanceToSegment(B,C,D)),min(DistanceToSegment(C,A,B),DistanceToSegment(D,A,B))); } //凸包: /**Andrew算法思路:首先按照先x后y从小到大排序(这个地方没有采用极角逆序排序,所以要进行两次扫描),删除重复的点后得到的序列p1,p2.....,然后把p1和p2放到凸包中。从p3开始,当新的 点在凸包“前进”方向的左边时继续,否则依次删除最近加入凸包的点,直到新点在左边;**/ //Goal[]数组模拟栈的使用; int ConvexHull(Point* P,int n,Point* Goal){ sort(P,P+n); int m = unique(P,P+n) - P; //对点进行去重; int cnt = 0; for(int i=0;i<m;i++){ //求下凸包; while(cnt>1 && dcmp(Cross(Goal[cnt-1]-Goal[cnt-2],P[i]-Goal[cnt-2])) <= 0) cnt--; Goal[cnt++] = P[i]; } int temp = cnt; for(int i=m-2;i>=0;i--){ //逆序求上凸包; while(cnt>temp && dcmp(Cross(Goal[cnt-1]-Goal[cnt-2],P[i]-Goal[cnt-2])) <= 0) cnt--; Goal[cnt++] = P[i]; } if(cnt > 1) cnt--; //减一为了去掉首尾重复的; return cnt; } double solve(Point* P1,Point* Q1,int Minid,int Maxid,int N,int M){ double temp,ret = 1e5; P1[N] = P1[0]; Q1[M] = Q1[0]; for(int i=0;i<N;i++){ while(temp = dcmp(Cross(Q1[Maxid]-Q1[Maxid+1],P1[Minid+1]-P1[Minid]))> 0) //这一步最难理解:要理解怎样才能使Q1[Maxid]Q1[Maxid+1]这条线段最接近线段P1[Minid+1]P1[Minid]; Maxid = (Maxid+1)%M; // 先以P1[Minid]为定点,旋转Q1[Maxid]; if(temp < 0) ret = min(ret,DistanceToSegment(Q1[Maxid],P1[Minid],P1[Minid+1])); else ret = min(ret,DistanceBetweenSegment(P1[Minid],P1[Minid+1],Q1[Maxid],Q1[Maxid+1]));\ Minid = (Minid + 1)%N; //再旋转P1[Minid]; } return ret; } Point read_point(){ Point A; scanf("%lf %lf",&A.x,&A.y); return A; } /*******************************分割线******************************/ Point P[maxn],Q[maxn]; Point P1[maxn],Q1[maxn]; //利用凸包算法使坐标逆时针化后的存储; int N,M; int Maxid,Minid; void GetMaxandMin(int& Maxid,int& Minid){ Maxid = 0; Minid = 0; for(int i=1;i<N;i++){ if(P1[i].y < P1[Minid].y) Minid = i; } for(int i=1;i<M;i++){ if(Q1[i].y > Q1[Maxid].y) Maxid = i; } } int main() { //freopen("E:\\acm\\input.txt","r",stdin); while(cin>>N>>M && N+M){ for(int i=0;i<N;i++) P[i] = read_point(); N = ConvexHull(P,N,P1); for(int i=0;i<M;i++) Q[i] = read_point(); M = ConvexHull(Q,M,Q1); GetMaxandMin(Maxid,Minid); double ans = solve(P1,Q1,Minid,Maxid,N,M); printf("%.5f\n",ans); } }