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);
    }
}
View Code

 

posted @ 2013-08-14 23:06  等待最好的两个人  阅读(238)  评论(0编辑  收藏  举报