zoukankan      html  css  js  c++  java
  • 「SOL」Bridge Across Islands(POJ)

    对踵点是旋转卡壳的核心


    # 题面

    > Link POJ 3608

    给两个凸包(凸多边形)(A,B),保证不相交。求两凸包的最小距离。

    数据规模:凸包的大小不超过 (10^4)


    # 解析

    求一个凸包的直径可以用旋转卡壳,那两个凸包呢?

    我们尝试将旋转的两条有向直线分别放在两个凸包上,并重新定义对踵点:

    「定义」

    对于凸包 $A$ 上的有向直线 $l=A_iA_{i+1}$,定义其方向为凸包的逆时针方向。

    则在 $l$ 左侧的点到 $l$ 的有向距离为正,右侧有向距离为负,有向距离的绝对值为该点到 $l$ 的欧几里得距离。

    $A_iA_{i+1}$ 的对踵点定义为 $B$ 上到 $l$ 的有向距离最大的点。

    举个例子,下图点 (a) 是直线 (u) 的对踵点,点 (b) 是直线 (v) 的对踵点:

    由于凸包之间的距离一定可以表示为其中一个凸包的点到另一个凸包的线段的距离(不一定是 (A) 上的点,(B) 上的线段;所以两种情况都要计算)。于是这样写旋转卡壳就可以求得凸包之间的距离。


    # 源代码

    /*Lucky_Glass*/
    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    
    namespace GEO{
    	#define cdou const double &
    	const double EPS=1e-16,INF=1e9;
    	inline int sgn(cdou key){return fabs(key)<EPS? 0:(key<0? -1:1);}
    	struct Vector{
    		double x,y;
    		Vector(cdou _x=0,cdou _y=0):x(_x),y(_y){}
    		inline Vector operator *(cdou k)const{return Vector(x*k,y*k);}
    		inline double len()const{return sqrt(x*x+y*y);}
    		inline friend double cross(const Vector &u,const Vector &v){
    			return u.x*v.y-u.y*v.x;
    		}
    		inline friend double dot(const Vector &u,const Vector &v){
    			return u.x*v.x+u.y*v.y;
    		}
    		inline Vector operator -()const{return Vector(-x,-y);}
    	};
    	struct Point{
    		double x,y;
    		Point(cdou _x=0,cdou _y=0):x(_x),y(_y){}
    		friend double distPoint(const Point &u,const Point &v){
    			return sqrt((u.x-v.x)*(u.x-v.x)+(u.y-v.y)*(u.y-v.y));
    		}
    		inline Point operator +(const Vector &v)const{return Point(x+v.x,y+v.y);}
    		inline Point operator -(const Vector &v)const{return Point(x-v.x,y-v.y);}
    		inline Vector operator -(const Point &v)const{return Vector(x-v.x,y-v.y);}
    		inline bool operator ==(const Point &v)const{return !sgn(x-v.x) && !sgn(y-v.y);}
    		inline bool operator !=(const Point &v)const{return sgn(x-v.x) || sgn(y-v.y);}
    	};
    	struct Line{
    		Point p;Vector d;
    		double ang;
    		Line(){}
    		Line(const Point &_p,const Vector &_d):p(_p),d(_d){}
    		Line(const Point &s,const Point &t):p(s),d(t-s){}
    		void getAngle(){ang=atan2(d.y,d.x);}
    	};
    	//notice that if the head/tail of a segment is on the other segment, it will return 'true'
    	bool ifSegInter(const Point &u1,const Point &u2,const Point &v1,const Point &v2){
    		int re1=sgn(cross(v1-u1,u2-u1)*cross(v2-u1,u2-u1)),
    			re2=sgn(cross(u1-v1,v2-v1)*cross(u2-v1,v2-v1));
    		return re1<=0 && re2<=0;
    	}
    	//1=left / 0=on / -1=right
    	int fixSide(const Point &s,const Point &t,const Point &p){return sgn(cross(t-s,p-s));}
    	int fixSide(const Line &l,const Point &p){return sgn(cross(l.d,p-l.p));}
    	Point getLineInter(const Line &u,const Line &v){
    		double h=cross(u.p-v.p,v.d),d=cross(u.d,v.d);
    		if(!sgn(d)) return u.p;
    		return u.p-u.d*(h/d);
    	}
    	bool cmpPointToX(const Point &u,const Point &v){return sgn(u.x-v.x)? sgn(u.x-v.x)<0:sgn(u.y-v.y)<0;}
    	bool cmpLineToAngle(const Line &u,const Line &v){return sgn(u.ang-v.ang)<0;}
    	/*
    	* notice:
    	* 'org' is 0-indexed;
    	* points on the edge of the convex is not counted
    	*/
    	bool buildConvex(Point *org,int n,Point *res,int &m){
    		if(n<=2) return false;
    		m=0;
    		sort(org,org+n,cmpPointToX);
    		for(int i=0;i<n;i++){
    			while(m>1 && fixSide(res[m-2],res[m-1],org[i])<=0) m--;
    			res[m++]=org[i];
    		}
    		int m0=m;
    		for(int i=n-2;~i;i--){
    			while(m>m0 && fixSide(res[m-2],res[m-1],org[i])<=0) m--;
    			res[m++]=org[i];
    		}
    		m--;
    		return m>2;
    	}
    	//notice: 'lin','rep' and 'rel' are 0-indexed
    	bool getSI(Line *lin,int n,Point *rep,Line *rel,int &m){
    		for(int i=0;i<n;i++) lin[i].getAngle();
    		sort(lin,lin+n,cmpLineToAngle);
    		int indl=0,indr=0;
    		rel[indr++]=lin[0];
    		for(int i=1;i<n;i++){
    			while(indr-indl>1 && fixSide(lin[i],rep[indr-2])<=0) indr--;
    			while(indr-indl>1 && fixSide(lin[i],rep[indl])<=0) indl++;
    			if(!sgn(cross(lin[i].d,rel[indr-1].d))){
    				if(fixSide(rel[indr-1],lin[i].p)>0){
    					rel[indr-1]=lin[i];
    					rep[indr-2]=getLineInter(rel[indr-1],rel[indr-2]);
    				}
    				continue;
    			}
    			rel[indr++]=lin[i];
    			rep[indr-2]=getLineInter(rel[indr-1],rel[indr-2]);
    		}
    		while(indr-indl>1 && fixSide(rel[indl],rep[indr-2])<=0) indr--;
    		rep[indr-1]=getLineInter(rel[indr-1],rel[indl]);
    		m=indr-indl;
    		if(m<=2) return false;
    		for(int i=indl;i<indr;i++) rel[i-indl]=rel[i],rep[i-indl]=rep[i];
    		return true;
    	}
    	double distLinePoint(const Line &l,const Point &p){
    		return fabs(cross(l.d,p-l.p))/l.d.len();
    	}
    	double distSegPoint(const Point &s1,const Point &s2,const Point &p){
    		if(sgn(dot(p-s1,s2-s1))>=0 && sgn(dot(p-s2,s1-s2))>=0) return distLinePoint(Line(s1,s2),p);
    		return min(distPoint(p,s1),distPoint(p,s2));
    	}
    }
    using namespace GEO;
    
    const int N=1e4+10;
    
    int n,m;
    Point p1[N],p2[N];
    
    #define debug(it) it.x,it.y
    
    double directDist(const Point &s,const Point &t,const Point &p){
    	return cross(t-s,p-s)/distPoint(s,t);
    }
    int main(){
    	// freopen("input.in","r",stdin);
    	while(~scanf("%d%d",&n,&m) && n && m){
    		for(int i=0;i<n;i++) scanf("%lf%lf",&p1[i].x,&p1[i].y);
    		for(int i=0;i<m;i++) scanf("%lf%lf",&p2[i].x,&p2[i].y);
    		if(fixSide(p1[0],p1[1],p1[2])<0) reverse(p1,p1+n);
    		if(fixSide(p2[0],p2[1],p2[2])<0) reverse(p2,p2+m);
    		p1[n]=p1[0],p2[m]=p2[0];
    		double ans=1e9;
    		for(int i=0,j=0;i<n;i++){
    			while(sgn(directDist(p1[i],p1[i+1],p2[j])-directDist(p1[i],p1[i+1],p2[j+1]))<0) j=(j+1)%m;
    			ans=min(ans,distSegPoint(p1[i],p1[i+1],p2[j]));
    			ans=min(ans,distSegPoint(p1[i],p1[i+1],p2[j+1]));
    		}
    		for(int i=0,j=0;i<m;i++){
    			while(sgn(directDist(p2[i],p2[i+1],p1[j])-directDist(p2[i],p2[i+1],p1[j+1]))<0) j=(j+1)%n;
    			ans=min(ans,distSegPoint(p2[i],p2[i+1],p1[j]));
    			ans=min(ans,distSegPoint(p2[i],p2[i+1],p1[j+1]));
    		}
    		printf("%.6f
    ",ans);
    	}
    	return 0;
    }
    

    THE END

    Thanks for reading!

    [egin{split} “ &你是春山 你是岁酒\ &你是自由 你是误谬\ &你是颠沛流离之后 我绮丽的愁 ”\ ——& ext{《你是我遥不可及的梦》 By 苍穹} end{split} ]

    > Link 你是我遥不可及的梦-Bilibili

    欢迎转载٩(๑❛ᴗ❛๑)۶,请在转载文章末尾附上原博文网址~
  • 相关阅读:
    install source mysql 5.7.9
    直接复制php的安装目录部署到其他服务器的时候,无法运行
    对硬盘进行分区时,GPT和MBR有什么区别?
    centos添加永久静态路由
    Windows2008R2安装远程桌面终端授权
    nginx搭建的cdn服务器的nginx.conf配置文件
    centos6.6配置vlan三层交换
    ESXI 6.0 嵌套虚拟化 Hyper-v
    VLAN的Hybrid和Trunk端口有何区别
    如何添加使用博客RSS订阅
  • 原文地址:https://www.cnblogs.com/LuckyGlass-blog/p/14384618.html
Copyright © 2011-2022 走看看