zoukankan      html  css  js  c++  java
  • POJ 3608 Bridge Across Islands(旋转卡壳,两凸包最短距离)

    Bridge Across Islands
    Time Limit: 1000MS   Memory Limit: 65536K
    Total Submissions: 7202   Accepted: 2113   Special Judge

    Description

    Thousands of thousands years ago there was a small kingdom located in the middle of the Pacific Ocean. The territory of the kingdom consists two separated islands. Due to the impact of the ocean current, the shapes of both the islands became convex polygons. The king of the kingdom wanted to establish a bridge to connect the two islands. To minimize the cost, the king asked you, the bishop, to find the minimal distance between the boundaries of the two islands.

    Input

    The input consists of several test cases.
    Each test case begins with two integers NM. (3 ≤ NM ≤ 10000)
    Each of the next N lines contains a pair of coordinates, which describes the position of a vertex in one convex polygon.
    Each of the next M lines contains a pair of coordinates, which describes the position of a vertex in the other convex polygon.
    A line with N = M = 0 indicates the end of input.
    The coordinates are within the range [-10000, 10000].

    Output

    For each test case output the minimal distance. An error within 0.001 is acceptable.

    Sample Input

    4 4
    0.00000 0.00000
    0.00000 1.00000
    1.00000 1.00000
    1.00000 0.00000
    2.00000 0.00000
    2.00000 1.00000
    3.00000 1.00000
    3.00000 0.00000
    0 0

    Sample Output

    1.00000

    Source

     
     
     
     
     
    经典算法。
     
    #include <stdio.h>
    #include <algorithm>
    #include <iostream>
    #include <string.h>
    #include <math.h>
    using namespace std;
    
    const double eps = 1e-8;
    int sgn(double x)
    {
        if(fabs(x) < eps)return 0;
        if(x < 0)return -1;
        else return 1;
    }
    struct Point
    {
        double x,y;
        Point(double _x = 0.0,double _y = 0.0)
        {
            x = _x;
            y = _y;
        }
        Point operator -(const Point &b)const
        {
            return Point(x - b.x, y - b.y);
        }
        double operator ^(const Point &b)const
        {
            return x*b.y - y*b.x;
        }
        double operator *(const Point &b)const
        {
            return x*b.x + y*b.y;
        }
        void input()
        {
            scanf("%lf%lf",&x,&y);
        }
    };
    struct Line
    {
        Point s,e;
        Line(){}
        Line(Point _s,Point _e)
        {
            s = _s;
            e = _e;
        }
    };
    double dist(Point a,Point b)
    {
        return sqrt((a-b)*(a-b));
    }
    Point NearestPointToLineSeg(Point P,Line L)
    {
        Point result;
        double t = ((P-L.s)*(L.e-L.s))/((L.e-L.s)*(L.e-L.s));
        if(t >= 0 && t <= 1)
        {
            result.x = L.s.x + (L.e.x - L.s.x)*t;
            result.y = L.s.y + (L.e.y - L.s.y)*t;
        }
        else
        {
            if(dist(P,L.s) < dist(P,L.e))
                result = L.s;
            else result = L.e;
        }
        return result;
    }
    /*
     * 求凸包,Graham算法
     * 点的编号0~n-1
     * 返回凸包结果Stack[0~top-1]为凸包的编号
     */
    const int MAXN = 10010;
    Point list[MAXN];
    int Stack[MAXN],top;
    //相对于list[0]的极角排序
    bool _cmp(Point p1,Point p2)
    {
        double tmp = (p1-list[0])^(p2-list[0]);
        if(sgn(tmp) > 0)return true;
        else if(sgn(tmp) == 0 && sgn(dist(p1,list[0]) - dist(p2,list[0])) <= 0)
            return true;
        else return false;
    }
    void Graham(int n)
    {
        Point p0;
        int k = 0;
        p0 = list[0];
        //找最下边的一个点
        for(int i = 1;i < n;i++)
        {
            if( (p0.y > list[i].y) || (p0.y == list[i].y && p0.x > list[i].x) )
            {
                p0 = list[i];
                k = i;
            }
        }
        swap(list[k],list[0]);
        sort(list+1,list+n,_cmp);
        if(n == 1)
        {
            top = 1;
            Stack[0] = 0;
            return;
        }
        if(n == 2)
        {
            top = 2;
            Stack[0] = 0;
            Stack[1] = 1;
            return ;
        }
        Stack[0] = 0;
        Stack[1] = 1;
        top = 2;
        for(int i = 2;i < n;i++)
        {
            while(top > 1 && sgn((list[Stack[top-1]]-list[Stack[top-2]])^(list[i]-list[Stack[top-2]])) <= 0)
                top--;
            Stack[top++] = i;
        }
    }
    //点p0到线段p1p2的距离
    double pointtoseg(Point p0,Point p1,Point p2)
    {
        return dist(p0,NearestPointToLineSeg(p0,Line(p1,p2)));
    }
    //平行线段p0p1和p2p3的距离
    double dispallseg(Point p0,Point p1,Point p2,Point p3)
    {
        double ans1 = min(pointtoseg(p0,p2,p3),pointtoseg(p1,p2,p3));
        double ans2 = min(pointtoseg(p2,p0,p1),pointtoseg(p3,p0,p1));
        return min(ans1,ans2);
    }
    //得到向量a1a2和b1b2的位置关系
    double Get_angle(Point a1,Point a2,Point b1,Point b2)
    {
        Point t = b1 - ( b2 - a1 );
        return (a2-a1)^(t-a1);
    }
    //旋转卡壳,求两个凸包的最小距离
    double rotating_calipers(Point p[],int np,Point q[],int nq)
    {
        int sp = 0, sq = 0;
        for(int i = 0;i < np;i++)
            if(sgn(p[i].y - p[sp].y) < 0)
                sp = i;
        for(int i = 0;i < nq;i++)
            if(sgn(q[i].y - q[sq].y) > 0)
                sq = i;
        double tmp;
        double ans = 1e99;
        for(int i = 0;i < np;i++)
        {
            while(sgn(tmp = Get_angle(p[sp],p[(sp+1)%np],q[sq],q[(sq+1)%nq])) < 0 )
                sq = (sq + 1)%nq;
            if(sgn(tmp) == 0)
                ans = min(ans,dispallseg(p[sp],p[(sp+1)%np],q[sq],q[(sq+1)%nq]));
            else ans = min(ans,pointtoseg(q[sq],p[sp],p[(sp+1)%np]));
            sp = (sp+1)%np;
        }
        return ans;
    }
    
    double solve(Point p[],int n,Point q[],int m)
    {
        return min(rotating_calipers(p,n,q,m),rotating_calipers(q,m,p,n));
    }
    Point p[MAXN],q[MAXN];
    int main()
    {
        int n,m;
        while(scanf("%d%d",&n,&m)==2)
        {
            if(n == 0 && m == 0)break;
            for(int i = 0;i < n;i++)
                list[i].input();
            Graham(n);
            n = top;
            for(int i = 0;i < n;i++)
                p[i] = list[Stack[i]];
            for(int i = 0;i < m;i++)
                list[i].input();
            Graham(m);
            m = top;
            for(int i = 0;i < m;i++)
                q[i] = list[Stack[i]];
            printf("%.5lf
    ",solve(p,n,q,m));
        }
        return 0;
    }
     
     
     
     
     
  • 相关阅读:
    ERP渠道管理添加验证和查询(二十二)
    SqlServer导入Excel数据
    WebApi帮助类
    SqlServer 递归查询树
    SqlServer查看表、存储过程、耗时查询、当前进程、开销较大的语句
    Excel上传找到错误数据类型
    索引Hint提示(INDEX Hint)
    SqlServer批量Sql一个表的数据导入到另一个数据
    SqlServer 游标
    JAVA运算符
  • 原文地址:https://www.cnblogs.com/kuangbin/p/3221911.html
Copyright © 2011-2022 走看看