神呐!!!!!!!
折腾了两天多!!!(其实有效率地去想去写应该一天吧)。。
原来的想法很淳朴(好吧,很笨),就是找出来卡壳的那两条线,然后算出每条线离它相应的那条边的角度,然后旋转。
分为三种情况
1、两条线都和凸包的边平行
2、和第一个凸包的某边平行
3、和第二个凸包的某边平行
然后这三种情况,分别算出来下次应该存在的卡壳,就这么下去。
一直TLE,现在才知道,因为计算角度,损失精度了,所以TLE了T T 。。。换了种判断选择哪条边的算法就A了T T 。。。
另一种是看南理学长的,大体思路都一样,只是细节处理,不用计算旋转的边,因为边必然是凸包上的边,只要每次循环的时候,判断选择的哪条边即可。
时间区别不大,唯一区别就是,一个200行,另一个140行T T 。。
短的:
#include <queue> #include <stack> #include <math.h> #include <stdio.h> #include <stdlib.h> #include <iostream> #include <limits.h> #include <string.h> #include <string> #include <algorithm> using namespace std; const int MAX = 10010; const double inf = 1e20*1.0; struct point{ double x,y;}; //点 struct line{ point a,b;}; const double eps = 1e-6; point c[MAX]; point stk1[MAX],stk2[MAX]; int t1,t2; bool dy(double x,double y) { return x > y + eps;} // x > y bool xy(double x,double y) { return x < y - eps;} // x < y bool dyd(double x,double y) { return x > y - eps;} // x >= y bool xyd(double x,double y) { return x < y + eps;} // x <= y bool dd(double x,double y) { return fabs( x - y ) < eps;} // x == y double crossProduct(point a,point b,point c) { return (c.x - a.x)*(b.y - a.y) - (b.x - a.x)*(c.y - a.y); } double disp2p(point a,point b) { return sqrt((a.x - b.x)*(a.x - b.x) + (a.y - b.y)*(a.y - b.y)); } bool cmp(point a,point b) // 排序 { double len = crossProduct(c[0],a,b); if( dd(len,0.0) ) return xy(disp2p(c[0],a),disp2p(c[0],b)); return xy(len,0.0); } void Graham(point c[],point stk[],int &top,int n) // 凸包 { int tmp = 0; for(int i=1; i<n; i++) if( xy(c[i].x,c[tmp].x) || dd(c[i].x,c[tmp].x) && xy(c[i].y,c[tmp].y) ) tmp = i; swap(c[0],c[tmp]); sort(c+1,c+n,cmp); stk[0] = c[0]; stk[1] = c[1]; top = 1; for(int i=2; i<n; i++) { while( xyd( crossProduct(stk[top],stk[top-1],c[i]), 0.0 ) && top >= 1 ) top--; stk[++top] = c[i]; } } double disp2seg(point p,point l1,point l2)// 点到线段的最短距离 { point t = p; t.x += l1.y - l2.y; t.y += l2.x - l1.x; if( dy(crossProduct(l1,t,p)*crossProduct(l2,t,p),0.0) ) return xy(disp2p(p,l1),disp2p(p,l2)) ? disp2p(p,l1) : disp2p(p,l2); return fabs( crossProduct(p,l1,l2) )/disp2p(l1,l2); } double rota_angle(point a1,point a2,point b1,point b2) //判断向量a1a2与b1b2的位置 { point t; t.x = b1.x - (b2.x - a1.x); t.y = b1.y - (b2.y - a1.y); return crossProduct(a1,a2,t); } double RC_mindish2h(point p[],point q[],int np,int nq) { int sp = 0,sq = 0; for(int i=1; i<np; i++) if( xy(p[i].y,p[sp].y) || dd(p[sp].y,p[i].y) && xy(p[i].x,p[sp].x) ) sp = i; //求得第一个凸包最左下角的点 for(int i=1; i<nq; i++) if( dy(q[i].y,q[sq].y) || dd(q[sq].y,q[i].y) && dy(q[i].x,q[sq].x) ) sq = i; //求得第二个凸包最右上角的点 int tp = sp,tq = sq; double ans = disp2p(p[sp],q[sq]); do { double len = rota_angle(p[sp],p[(sp+1)%np],q[sq],q[(sq+1)%nq]); if( dd(len,0.0) ) // 卡壳正好和俩凸包的边重合 { ans = min(ans,disp2seg(p[sp],q[sq],q[(sq+1)%nq])); ans = min(ans,disp2seg(p[(sp+1)%np],q[sq],q[(sq+1)%nq])); ans = min(ans,disp2seg(q[sq],p[sp],p[(sp+1)%np])); ans = min(ans,disp2seg(q[(sq+1)%nq],p[sp],p[(sp+1)%np])); sp++; sp %= np; sq++; sq %= nq; } else if( xy(len,0.0) ) // 卡壳和第一个凸包的边重合 { ans = min(ans,disp2seg(q[sq],p[sp],p[(sp+1)%np])); sp++; sp %= np; } else { ans = min(ans,disp2seg(p[sp],q[sq],q[(sq+1)%nq]));// 卡壳和第二个凸包的边重合 sq++; sq %= nq; } }while( !(tp == sp && tq == sq) ); return ans; } int main() { int n,m; while( ~scanf("%d%d",&n,&m) && n ) { for(int i=0; i<n; i++) scanf("%lf%lf",&c[i].x,&c[i].y); Graham(c,stk1,t1,n); for(int i=0; i<m; i++) scanf("%lf%lf",&c[i].x,&c[i].y); Graham(c,stk2,t2,m); double ans = RC_mindish2h(stk1,stk2,t1+1,t2+1); printf("%.5lf/n",ans); } return 0; }
长的:
#include <queue> #include <stack> #include <math.h> #include <stdio.h> #include <stdlib.h> #include <iostream> #include <limits.h> #include <string.h> #include <string> #include <algorithm> using namespace std; const int MAX = 10010; const double inf = 1e20*1.0; struct point{ double x,y;}; //点 struct line{ point a,b;}; const double eps = 1e-6; point c[MAX]; point stk1[MAX],stk2[MAX]; int t1,t2; bool dy(double x,double y) { return x > y + eps;} // x > y bool xy(double x,double y) { return x < y - eps;} // x < y bool dyd(double x,double y) { return x > y - eps;} // x >= y bool xyd(double x,double y) { return x < y + eps;} // x <= y bool dd(double x,double y) { return fabs( x - y ) < eps;} // x == y double crossProduct(point a,point b,point c) { return (c.x - a.x)*(b.y - a.y) - (b.x - a.x)*(c.y - a.y); } double disp2p(point a,point b) { return sqrt((a.x - b.x)*(a.x - b.x) + (a.y - b.y)*(a.y - b.y)); } bool cmp(point a,point b) // 排序 { double len = crossProduct(c[0],a,b); if( dd(len,0.0) ) return xy(disp2p(c[0],a),disp2p(c[0],b)); return xy(len,0.0); } void Graham(point c[],point stk[],int &top,int n) { int tmp = 0; for(int i=1; i<n; i++) if( xy(c[i].x,c[tmp].x) || dd(c[i].x,c[tmp].x) && xy(c[i].y,c[tmp].y) ) tmp = i; swap(c[0],c[tmp]); sort(c+1,c+n,cmp); stk[0] = c[0]; stk[1] = c[1]; top = 1; for(int i=2; i<n; i++) { while( xyd( crossProduct(stk[top],stk[top-1],c[i]), 0.0 ) && top >= 1 ) top--; stk[++top] = c[i]; } } double disp2seg(point p,point l1,point l2) { point t = p; t.x += l1.y - l2.y; t.y += l2.x - l1.x; if( dy(crossProduct(l1,t,p)*crossProduct(l2,t,p),0.0) ) return xy(disp2p(p,l1),disp2p(p,l2)) ? disp2p(p,l1) : disp2p(p,l2); return fabs( crossProduct(p,l1,l2) )/disp2p(l1,l2); } double rota_angle(point a1,point a2,point b1,point b2) { point t; t.x = b1.x - (b2.x - a1.x); t.y = b1.y - (b2.y - a1.y); return crossProduct(a1,a2,t); } point rota_point(point a1,point a2,point b1)//向量a1a2原点平移到b1这里后的点 { point t; t.x = b1.x - ( a2.x - a1.x ); t.y = b1.y - ( a2.y - a1.y ); return t; } double RC_mindish2h(point p[],point q[],int np,int nq) { int sp = 0,sq = 0; for(int i=1; i<np; i++) if( xy(p[i].y,p[sp].y) || dd(p[sp].y,p[i].y) && xy(p[i].x,p[sp].x) ) sp = i; //求得第一个凸包最左下角的点 for(int i=1; i<nq; i++) if( dy(q[i].y,q[sq].y) || dd(q[sq].y,q[i].y) && dy(q[i].x,q[sq].x) ) sq = i; //求得第二个凸包最右上角的点 int tp = sp,tq = sq; point lp,lq; // 初始化两条线 double ans = disp2p(p[sp],q[sq]); if( xy(rota_angle(p[(sp+1)%np],p[(sp+2)%np],q[(sq+1)%nq],q[(sq+2)%nq]),0.0) ) { // 确定下一次的卡壳位置 lp = p[(sp+1)%np]; lq = rota_point(p[sp],lp,q[sq]); } else { lq = q[(sq+1)%nq]; lp = rota_point(q[sq],lq,p[sp]); } do { if( dd( crossProduct(p[sp],p[(sp+1)%np],lp),0.0 ) && // 卡壳正好和俩凸包的边重合 dd( crossProduct(q[sq],q[(sq+1)%nq],lq),0.0) ) { ans = min(ans,disp2seg(p[sp],q[sq],q[(sq+1)%nq])); ans = min(ans,disp2seg(p[(sp+1)%np],q[sq],q[(sq+1)%nq])); ans = min(ans,disp2seg(q[sq],p[sp],p[(sp+1)%np])); ans = min(ans,disp2seg(q[(sq+1)%nq],p[sp],p[(sp+1)%np])); if( xy(rota_angle(p[(sp+1)%np],p[(sp+2)%np],q[(sq+1)%nq],q[(sq+2)%nq]),0.0) ) { // 确定下一次的卡壳位置 lp = p[(sp+2)%np]; sp++; sp %= np; lq = rota_point(p[sp],lp,q[(sq+1)%nq]); sq++; sq %= nq; } else { lq = q[(sq+2)%nq]; sq++; sq %= nq; lp = rota_point(q[sq],lq,p[(sp+1)%np]); sp++; sp %= np; } } else if( dd( crossProduct(p[sp],p[(sp+1)%np],lp),0.0 ) ) // 卡壳和第一个凸包的边重合 { ans = min(ans,disp2seg(q[sq],p[sp],p[(sp+1)%np])); if( xy(rota_angle(p[(sp+1)%np],p[(sp+2)%np],q[sq],q[(sq+1)%nq]),0.0) ) { // 确定下一次的卡壳位置 lp = p[(sp+2)%np]; sp++; sp %= np; lq = rota_point(p[sp],lp,q[sq]); } else { lq = q[(sq+1)%nq]; sp++; sp %= np; lp = rota_point(q[sq],lq,p[sp]); } } else { ans = min(ans,disp2seg(p[sp],q[sq],q[(sq+1)%nq]));// 卡壳和第二个凸包的边重合 if( xy(rota_angle(p[sp],p[(sp+1)%np],q[(sq+1)%nq],q[(sq+2)%nq]),0.0) ) { // 确定下一次的卡壳位置 lp = p[(sp+1)%np]; sq++; sq %= nq; lq = rota_point(p[sp],lp,q[sq]); } else { lq = q[(sq+2)%nq]; sq++; sq %= nq; lp = rota_point(q[sq],lq,p[sp]); } } }while( !(tp == sp && tq == sq) ); return ans; } int main() { int n,m; while( ~scanf("%d%d",&n,&m) && n ) { for(int i=0; i<n; i++) scanf("%lf%lf",&c[i].x,&c[i].y); Graham(c,stk1,t1,n); for(int i=0; i<m; i++) scanf("%lf%lf",&c[i].x,&c[i].y); Graham(c,stk2,t2,m); double ans = RC_mindish2h(stk1,stk2,t1+1,t2+1); printf("%.5lf/n",ans); } return 0; }