poj 3608(旋转卡壳求解两凸包之间的最短距离)

Bridge Across Islands

Time Limit: 1000MS   Memory Limit: 65536K
Total Submissions: 9768   Accepted: 2866   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 N, M. (3 ≤ N, M ≤ 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

引自:http://blog.csdn.net/acmaker/article/details/3178696两个凸多边形 PQ 之间的最小距离由多边形间的对踵点对确立。 存在凸多边形间的三种多边形间的对踵点对, 因此就有三种可能存在的最小距离模式:
  1. “顶点-顶点”的情况
  2. “顶点-边”的情况
  3. “边-边”的情况
换句话说, 确定最小距离的点对不一定必须是顶点。 下面的三个图例表明了以上结论: 

 

 

给定结果, 一个基于旋转卡壳的算法自然而然的产生了: 考虑如下的算法, 算法的输入是两个分别有 m 和 n 个顺时针给定顶点的凸多边形 P 和 Q。
    1. 计算 Py 坐标值最小的顶点(称为 yminP ) 和 Qy 坐标值最大的顶点(称为 ymaxQ)。
    2. 为多边形在 yminPymaxQ 处构造两条切线 LPLQ 使得他们对应的多边形位于他们的右侧。 此时 LPLQ 拥有不同的方向, 并且 yminPymaxQ 成为了多边形间的一个对踵点对。
    3. 计算距离(yminP,ymaxQ) 并且将其维护为当前最小值。
    4. 顺时针同时旋转平行线直到其中一个与其所在的多边形的边重合。
    5. 如果只有一条线与边重合, 那么只需要计算“顶点-边”对踵点对和“顶点-顶点”对踵点对距离。 都将他们与当前最小值比较, 如果小于当前最小值则进行替换更新。 如果两条切线都与边重合, 那么情况就更加复杂了。 如果边“交叠”, 也就是可以构造一条与两条边都相交的公垂线(但不是在顶点处相交), 那么就计算“边-边”距离。 否则计算三个新的“顶点-顶点”对踵点对距离。 所有的这些距离都与当前最小值进行比较, 若小于当前最小值则更新替换。
    6. 重复执行步骤4和步骤5, 直到新的点对为(yminP,ymaxQ)。
    7. 输出最小距离。

模板如下:

/*
叉积的一个非常重要的性质是可以通过它的符号来判断两矢量相互之间的顺逆时针关系:
            若 P * Q > 0,则 P 在 Q 的顺时针方向;
            若 P * Q < 0, 则 P 在 Q 的逆时针方向;
            若 P * Q = 0,则 P 与 Q 共线,但不确定 P, Q 的方向是否相同;
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <math.h>
#include <algorithm>
using namespace std;
const double esp = 1e-10;
const int N = 10005;
struct Point
{
    double x,y;
} p[N],q[N];
int n,m;
///叉积
double mult_cross(Point a,Point b,Point c)
{
    return (a.x-c.x)*(b.y-c.y)-(b.x-c.x)*(a.y-c.y);
}
///点积
double mult_point(Point a,Point b,Point c){
    return (a.x-c.x)*(b.x-c.x)+(a.y-c.y)*(b.y-c.y);
}
///距离
double dis(Point a,Point b)
{
    return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
///将点集按照顺时钟排序
void clockwise_sort(Point p[],int n){
    for(int i=0;i<n-2;i++){
        double tmp = mult_cross(p[i+1],p[i+2],p[i]);
        if(tmp>0) return;
        else if(tmp<0){
            reverse(p,p+n);
            return;
        }
    }
}
///点c到直线ab的最短距离
double GetDist(Point a,Point b,Point c){
    if(dis(a,b)<esp) return dis(b,c); ///a,b是同一个点
    if(mult_point(b,c,a)<-esp) return dis(a,c); ///投影
    if(mult_point(a,c,b)<-esp) return dis(b,c);
    return fabs(mult_cross(b,c,a)/dis(a,b));

}
///求一条线段ab的两端点到另外一条线段bc的距离,反过来一样,共4种情况
double MinDist(Point a,Point b,Point c,Point d){
    return min(min(GetDist(a,b,c),GetDist(a,b,d)),min(GetDist(c,d,a),GetDist(c,d,b)));
}

double min_PQ(Point p[],Point q[],int n,int m){
    int yminP = 0,ymaxQ=0;
    for(int i=1;i<n;i++){  ///找到点集p组成的凸包的左下角
        if(p[i].y<p[yminP].y||(p[i].y==p[yminP].y)&&(p[i].x<p[yminP].x)) yminP = i;
    }
    for(int i=1;i<m;i++){  ///找到点集q组成的凸包的右上角
        if(q[i].y>q[ymaxQ].y||(q[i].y==q[ymaxQ].y)&&(q[i].x>q[ymaxQ].x)) ymaxQ = i;
    }
    double ans = dis(p[yminP],q[ymaxQ]); ///距离(yminP,ymaxQ)维护为当前最小值。
    p[n]=p[0],q[m]=q[0];
    for(int i=0;i<n;i++){
        double tmp;
        while(tmp=(mult_cross(q[ymaxQ+1],p[yminP],p[yminP+1])-mult_cross(q[ymaxQ],p[yminP],p[yminP+1]))>esp)
            ymaxQ = (ymaxQ+1)%m;
        if(tmp<-esp) ans = min(ans,GetDist(p[yminP],p[yminP+1],q[ymaxQ]));
        else ans=min(ans,MinDist(p[yminP],p[yminP+1],q[ymaxQ],q[ymaxQ+1]));
        yminP = (yminP+1)%n;
    }
    return ans;
}
int main()
{
    while(scanf("%d%d",&n,&m)!=EOF,n+m)
    {
        for(int i=0; i<n; i++)
        {
            scanf("%lf%lf",&p[i].x,&p[i].y);
        }
        for(int i=0; i<m; i++)
        {
            scanf("%lf%lf",&q[i].x,&q[i].y);
        }
        clockwise_sort(p,n);
        clockwise_sort(q,m);
        double ans = min(min_PQ(p,q,n,m),min_PQ(q,p,m,n));
        printf("%.5lf\n",ans);
    }
    return 0;
}
时间: 2024-08-12 04:36:53

poj 3608(旋转卡壳求解两凸包之间的最短距离)的相关文章

poj 3608 Bridge Across Islands 两凸包间最近距离

1 /** 2 旋转卡壳,, 3 **/ 4 #include <iostream> 5 #include <algorithm> 6 #include <cmath> 7 #include <cstdio> 8 using namespace std; 9 10 const double eps = 1e-8; 11 struct point { 12 double x,y; 13 point(double x=0,double y =0):x(x),y(

Bridge Across Islands POJ - 3608 旋转卡壳求凸包最近距离

\(\color{#0066ff}{题目描述}\) 几千年前,有一个小王国位于太平洋的中部.王国的领土由两个分离的岛屿组成.由于洋流的冲击,两个岛屿的形状都变成了凸多边形.王国的国王想建立一座桥来连接这两个岛屿.为了把成本降到最低,国王要求你,主教,找到两个岛屿边界之间最小的距离. \(\color{#0066ff}{输入格式}\) 输入由几个测试用例组成. 每个测试用两个整数n,m(3≤n,m≤10000)开始 接下来的n行中的每一行都包含一对坐标,用来描述顶点在一个凸多边形中的位置. 下一条

POJ3608(旋转卡壳--求两凸包的最近点对距离)

分析:以下内容来自:http://blog.csdn.net/acmaker/article/details/3178696 考虑如下的算法, 算法的输入是两个分别有m和n个顺时针给定顶点的凸多边形P和Q. 1.计算P上y坐标值最小的顶点(称为 yminP )和Q上y坐标值最大的顶点(称为 ymaxQ). 2.为多边形在 yminP 和 ymaxQ 处构造两条切线 LP 和 LQ 使得他们对应的多边形位于他们的右侧.   此时 LP 和 LQ 拥有不同的方向, 并且 yminP 和 ymaxQ

POJ 2187 旋转卡壳 + 水平序 Graham 扫描算法

水平序 Graham 扫描算法: 计算二维凸包的时候可以用到,Graham 扫描算法有水平序和极角序两种. 极角序算法能一次确定整个凸包, 但是计算极角需要用到三角函数,速度较慢,精度较差,特殊情况较多. 水平序算法需要扫描两次,但排序简单,讨论简单,不易出错. [算法流程] 1.对顶点按x为第一关键字,y为第二关键字进行排序. 2.准备一个空栈,并将前两个点压入栈. 3.对于每一个顶点A,只要栈顶中还至少两个顶点,记栈顶为T,栈中第二个为U. 若UT(向量) * TA(向量) <= 0, 则将

Gym - 101635K:Blowing Candles (简单旋转卡壳,求凸包宽度)

题意:给定N个点,用矩形将所有点覆盖,要求矩形宽度最小. 思路:裸体,旋转卡壳去rotate即可. 最远距离是点到点:宽度是点到边. #include<bits/stdc++.h> #define ll long long #define rep(i,a,b) for(int i=a;i<=b;i++) #define RC rotating_calipers using namespace std; const int maxn=400010; struct point{ ll x,y

poj 2079 Triangle(旋转卡壳)

Triangle Time Limit: 3000MS   Memory Limit: 30000K Total Submissions: 8917   Accepted: 2650 Description Given n distinct points on a plane, your task is to find the triangle that have the maximum area, whose vertices are from the given points. Input

模板 凸包 旋转卡壳

模板  凸包  旋转卡壳 lrj <训练指南> P272 对于个点按照 x 从小到大排序,再按照 y 点从小到大排序,删除重复的点后,得到序列 p0,p1,p2..., 把 p0 和 p1 放入凸包. 从p2开始,当新点在凸包“前进”方向的左边时继续,否则依次删除最近加入凸包的点,直到新点在左边 PS:判断用叉积即可 1 /******************************************** 2 计算凸包,输入点数组 p, 个数为 n , 输出点数组 ch. 函数返回凸包顶

UVA 4728 Squares(凸包+旋转卡壳)

题目链接:http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=17267 [思路] 凸包+旋转卡壳 求出凸包,用旋转卡壳算出凸包的直径即可. [代码] 1 #include<cstdio> 2 #include<vector> 3 #include<iostream> 4 #include<algorithm> 5 using namespace std; 6 7 struct Pt { 8

【POJ 3608】Bridge Across Islands

Bridge Across Islands Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 8853   Accepted: 2603   Special Judge Description Thousands of thousands years ago there was a small kingdom located in the middle of the Pacific Ocean. The territory