POJ 3608 Bridge Across Islands(两个凸包最近距离,旋转卡壳)
题目:两个不相交的多边形,求最近距离。
http://poj.org/problem?id=3608
这里有详细的讲解:http://cgm.cs.mcgill.ca/~orm/mind2p.html
这是找到第一个凸包的左下角点,找到第二个凸的右下角的点,两条平行线卡壳。
然后开始旋转,分为三种情况:
两条线和两个凸包都重合,以及和其中一个凸包重合。
转变成线段与线段的最短距离,以及点与线段的最短距离,注意线段与线段的距离不等价于直线与直线的最短距离。
十分蛋疼的一题,估计很少有人1A,细节很多。
[cpp]
#include<iostream>
#include<fstream>
#include<iomanip>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<cmath>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<string>
#include<vector>
#include<sstream>
#include<ctime>
#include<cassert>
#define LL long long
#define eps 1e-8
#define inf 999999.0
#define zero(a) abs(a)<eps
#define N 20
#define pi acos(-1.0)
using namespace std;
struct Point{
double x,y;
}p[10005],q[10005],pos;
vector<Point >s1,s2;
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 xmul(Point p0,Point p1,Point p2){
return (p1.x-p0.x)*(p2.y-p0.y)-(p2.x-p0.x)*(p1.y-p0.y);
}
//Graham_scan求凸包
bool cmp(Point p1,Point p2){
if(xmul(pos,p1,p2)>eps) return true;
else if(zero(xmul(pos,p1,p2))&&dist(pos,p1)<dist(pos,p2)) return true;
return false;
}
void Graham_scan(vector<Point>&s,Point p[],int n){
for(int i=1;i<n;i++)
if(p[i].y<p[0].y||(zero(p[i].y==p[0].y)&&p[i].x<p[0].x))
swap(p[i],p[0]);
pos=p[0];
sort(p+1,p+n,cmp);
s.clear();
s.push_back(p[0]);s.push_back(p[1]);s.push_back(p[2]);
for(int i=3;i<n;i++){
while(s.size()>=2&&xmul(s[s.size()-2],s[s.size()-1],p[i])<eps) s.pop_back();
s.push_back(p[i]);
}
}
//得到向量a1b1和a2b2的位置关系
double Get_angle(Point a1,Point b1,Point a2,Point b2){
Point t;
t.x=a2.x-(b2.x-a1.x);
t.y=a2.y-(b2.y-a1.y);
return xmul(a1,b1,t);
}
double Dist_Point_Seg(Point p,Point a,Point b){
Point t=p;
t.x+=a.y-b.y;t.y+=b.x-a.x;
if(xmul(a,t,p)*xmul(b,t,p)>eps)
return dist(p,a)+eps<dist(p,b)?dist(p,a):dist(p,b);
else
return fabs(xmul(p,a,b))/dist(a,b);
}
double Min_Dist_Two_Polygons(vector<Point>s1,vector<Point>s2){
int na=s1.size(),nb=s2.size();
int lp=0,lq=0;
for(int i=1;i<na;i++)
if(s1[i].y<s1[lp].y||(zero(s1[i].y-s1[lp].y)&&s1[i].x<s1[lp].x))
lp=i;
for(int i=1;i<nb;i++)
if(s2[i].y>s2[lq].y||(zero(s2[i].y-s2[lq].y)&&s2[i].x>s2[lq].x))
lq=i;
s1.push_back(s1[0]);s2.push_back(s2[0]);
double ans=dist(s1[lp],s2[lq]);
int tp=lp,tq=lq;
do{
double angle=Get_angle(s1[tp],s1[tp+1],s2[tq],s2[tq+1]);
//和两个凸包的边都重合
if(zero(angle)){
ans=min(ans,Dist_Point_Seg(s1[tp],s2[tq],s2[tq+1]));
ans=min(ans,Dist_Point_Seg(s1[tp+1],s2[tq],s2[tq+1]));
ans=min(ans,Dist_Point_Seg(s2[tq],s1[tp],s1[tp+1]));
ans=min(ans,Dist_Point_Seg(s2[tq+1],s1[tp],s1[tp+1]));
tp=(tp+1)%na;tq=(tq+1)%nb;
}
else{
//和第二个凸包的边重合
if(angle<-eps){
ans=min(ans,Dist_Point_Seg(s1[tp],s2[tq],s2[tq+1]));
tq=(tq+1)%nb;
}
//和第一个凸包的边重合
else{
ans=min(ans,Dist_Point_Seg(s2[tq],s1[tp],s1[tp+1]));
tp=(tp+1)%na;
}
}
}while(!(lp==tp&&lq==tq));
return ans;
}
int main(){
int n,m;
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);
Graham_scan(s1,p,n);
for(int i=0;i<m;i++)
scanf("%lf%lf",&q[i].x,&q[i].y);
Graham_scan(s2,q,m);
printf("%.5f\n",Min_Dist_Two_Polygons(s1,s2));
}
return 0;
}
补充:软件开发 , C++ ,