zoukankan      html  css  js  c++  java
  • 「题解」POI2005 AKC-Special Forces Manoeuvres

    本文将同步发布于:

    题目

    题目链接:洛谷 P3428官网

    题意简述

    给定 (n) 个圆 ((x_i,y_i,r_i)),每个圆对应一个点集 (S_i=left{(x,y)mid (x-x_i)^2+(y-y_i)^2leq r_i^2 ight})

    求一个最小的 (i) 满足 (cap_{j=1}^i S_j=varnothing);如果无解输出 NIE

    题解

    简单又自然的随机化

    我们考虑枚举 (i),然后判定 (S_{1sim i}) 的交集是否为空。

    如何判定呢?我们想到一个简单的方法,我们随机一些在圆的边界上的点,只需要判定这些点是否存至少在一个点在所有圆内即可。

    这种方法简单又自然,但是随机化算法正确率不高,这远远不够。

    研究几何性质

    如果做计算几何题而抛弃几何性质,所得到的做法往往是劣解。

    继续沿着上面的思路,我们同样考虑枚举 (i),然后判定 (S_{1sim i}) 的交集是否为空。

    不同的是,我们定义一个交集中横坐标最大的点为代表点(代表点只会有一个,这是因为圆是凸集,凸集的交集还是凸集)。

    我们发现,如果一些圆的交集非空,那么其代表点一定满足:它是所有圆两两交集的代表中横坐标最小的那个。

    证明十分显然,考虑交集的意义即可。

    最后的结论

    综上所述,对于一个 (i),我们只需要求出 (1sim i-1)(i) 的代表点即可,如果所有代表点中横坐标最小的那一个在所有的圆内,那么其合法,否则不合法,换言之,答案为 (i)

    我们考虑证明这个结论:

    • 若没有交集,则这个点必然不合法,符合我们的预期;
    • 若有交集,则我们需要证明这个点是交集的代表点。
      • 假设其不是交集的代表点,则交集的代表点可能在其左右;
      • 左边:不可能,若交集存在,则代表点的横坐标 (geq) 当前点横坐标。
      • 右边:不可能,考虑当前点在 (S_acap S_b) 中得到,那么所有 (xgeq) 当前点横坐标的点均被交集抛弃,因此代表点的横坐标 (leq) 当前点横坐标。
      • 由夹逼过程可知结论正确。

    这个算法的时间复杂度为 (Theta(n^2))

    参考程序

    下面我们来解决两圆求交的问题。

    下面介绍一下两种方法:余弦定理和相似三角形。

    余弦定理

    用余弦定理求解需要用到三角函数,常数大,精度差。

    我们考虑下图:

    2WRtN4.png

    ( riangle{ACB}) 运用余弦定理,得到 (r_a^2+d^2-2dr_acosalpha=r_b^2),进而求出 (alpha=arccosleft(frac{r_a^2+d^2-r_b2}{2dr_a} ight))

    然后我们再求出基准角 (eta),显然 (eta= exttt{atan2}(y_b-y_a,x_b-x_a))

    因此,我们得到了 (C,D) 两点的对 (A) 的极角为 (eta+alpha)(eta-alpha)

    对于极角为 ( heta),极径为 (r_a) 的点,我们得出其对应点的坐标为 ((r_acos heta,r_asin heta))

    #include<bits/stdc++.h>
    using namespace std;
    #define reg register
    typedef long long ll;
    
    const double eps=1e-6;
    
    inline int sgn(reg double x){
    	if(fabs(x)<eps)
    		return 0;
    	else
    		return x<0?-1:1;
    }
    
    inline double sqr(reg double x){
    	return x*x;
    }
    
    const int MAXN=2e3+5;
    
    struct Vector{
    	double x,y;
    	inline Vector(reg double x=0,reg double y=0):x(x),y(y){
    		return;
    	}
    	inline Vector operator+(const Vector& a)const{
    		return Vector(x+a.x,y+a.y);
    	}
    	inline Vector operator-(const Vector& a)const{
    		return Vector(x-a.x,y-a.y);
    	}
    	inline Vector operator*(const double a)const{
    		return Vector(x*a,y*a);
    	}
    };
    
    inline double dot(const Vector& a,const Vector& b){
    	return a.x*b.x+a.y*b.y;
    }
    
    inline double cross(const Vector& a,const Vector& b){
    	return a.x*b.y-a.y*b.x;
    }
    
    typedef Vector Point;
    
    inline double getDis2(const Point& a,const Point& b){
    	return dot(a-b,a-b);
    }
    
    inline double getDis(const Point& a,const Point& b){
    	return sqrt(getDis2(a,b));
    }
    
    inline bool isEmpty(const Point& a){
    	return a.x!=a.x||a.y!=a.y;
    }
    
    struct Circle{
    	Point o;
    	double r;
    	inline bool contain(const Point& x)const{
    		return sgn(sqr(r)-getDis2(x,o))>=0;
    	}
    	inline Point getRig(void)const{
    		return o+Vector(r,0);
    	}
    };
    
    inline bool isCon(const Circle& a,const Circle& b){
    	return sgn(sqr(a.r-b.r)-getDis2(a.o,b.o))>=0;
    }
    
    inline bool isSep(const Circle& a,const Circle& b){
    	return sgn(getDis2(a.o,b.o)-sqr(a.r+b.r))>0;
    }
    
    inline Point getPot(const Circle &a,const Circle &b){
    	if(isCon(a,b))
    		if(sgn(b.getRig().x-a.getRig().x)>0)
    			return a.getRig();
    		else
    			return b.getRig();
    	else if(isSep(a,b))
    		return Point(nan(""),nan(""));
    	else{
    		if(a.contain(b.getRig()))
    			return b.getRig();
    		else if(b.contain(a.getRig()))
    			return a.getRig();
    		else{
    			reg double d=getDis(a.o,b.o);
    			reg double ang=acos(((sqr(a.r)+sqr(d))-sqr(b.r))/(2*a.r*d));
    			reg double delta=atan2(b.o.y-a.o.y,b.o.x-a.o.x);
    			reg double ang1=delta+ang,ang2=delta-ang;
    			Point p1=a.o+Vector(cos(ang1)*a.r,sin(ang1)*a.r);
    			Point p2=a.o+Vector(cos(ang2)*a.r,sin(ang2)*a.r);
    			Point res;
    			if(sgn(p2.x-p1.x)>0)
    				res=p2;
    			else
    				res=p1;
    			return res;
    		}
    	}
    }
    
    int n;
    Circle a[MAXN];
    
    int main(void){
    	scanf("%d",&n);
    	Point lef(0,0);
    	for(reg int i=1;i<=n;++i){
    		static int x,y,r;
    		scanf("%d%d%d",&x,&y,&r);
    		a[i].o=Point(x,y),a[i].r=r;
    		if(i==2)
    			lef=getPot(a[1],a[2]);
    		else if(i>2){
    			for(reg int j=1;j<i&&!isEmpty(lef);++j){
    				Point tmp=getPot(a[i],a[j]);
    				if(isEmpty(tmp)||tmp.x<=lef.x)
    					lef=tmp;
    			}
    			for(reg int j=1;j<=i&&!isEmpty(lef);++j)
    				if(!a[j].contain(lef))
    					lef=Point(nan(""),nan(""));
    		}
    		if(isEmpty(lef)){
    			printf("%d
    ",i);
    			return 0;
    		}
    	}
    	puts("NIE");
    	return 0;
    }
    

    相似三角形

    2WRN4J.png

    如上图,我们设 (a=|AG|)(b=|BG|)(h=|CG|)

    那么我们有:

    [egin{cases}r_a^2=a^2+h^2\r_b^2=b^2+h^2\a+b=dend{cases} ]

    那么我们有:

    [a=frac{r_a^2+d^2-r_b^2}{2d} ]

    然后考虑 ( riangle AIBsim riangle CHG),我们有:

    [(y_b-y_a)h=d(y_c-y_g) ]

    我们可由此解出坐标,其他同理可算出。

    #include<bits/stdc++.h>
    using namespace std;
    #define reg register
    typedef long long ll;
    
    const double eps=1e-6;
    
    inline int sgn(reg double x){
    	if(fabs(x)<eps)
    		return 0;
    	else
    		return x<0?-1:1;
    }
    
    inline double sqr(reg double x){
    	return x*x;
    }
    
    const int MAXN=2e3+5;
    
    struct Vector{
    	double x,y;
    	inline Vector(reg double x=0,reg double y=0):x(x),y(y){
    		return;
    	}
    	inline Vector operator+(const Vector& a)const{
    		return Vector(x+a.x,y+a.y);
    	}
    	inline Vector operator-(const Vector& a)const{
    		return Vector(x-a.x,y-a.y);
    	}
    	inline Vector operator*(const double a)const{
    		return Vector(x*a,y*a);
    	}
    };
    
    inline double dot(const Vector& a,const Vector& b){
    	return a.x*b.x+a.y*b.y;
    }
    
    inline double cross(const Vector& a,const Vector& b){
    	return a.x*b.y-a.y*b.x;
    }
    
    typedef Vector Point;
    
    inline double getDis2(const Point& a,const Point& b){
    	return dot(a-b,a-b);
    }
    
    inline double getDis(const Point& a,const Point& b){
    	return sqrt(getDis2(a,b));
    }
    
    inline bool isEmpty(const Point& a){
    	return isnan(a.x)||isnan(a.y);
    }
    
    struct Circle{
    	Point o;
    	double r;
    	inline bool contain(const Point& x)const{
    		return sgn(sqr(r)-getDis2(x,o))>=0;
    	}
    	inline Point getRig(void)const{
    		return o+Vector(r,0);
    	}
    };
    
    inline bool isCon(const Circle& a,const Circle& b){
    	return sgn(sqr(a.r-b.r)-getDis2(a.o,b.o))>=0;
    }
    
    inline bool isSep(const Circle& a,const Circle& b){
    	return sgn(getDis2(a.o,b.o)-sqr(a.r+b.r))>0;
    }
    
    inline Point getPot(const Circle &a,const Circle &b){
    	if(isCon(a,b))
    		if(sgn(b.getRig().x-a.getRig().x)>0)
    			return a.getRig();
    		else
    			return b.getRig();
    	else if(isSep(a,b))
    		return Point(nan(""),nan(""));
    	else{
    		if(a.contain(b.getRig()))
    			return b.getRig();
    		else if(b.contain(a.getRig()))
    			return a.getRig();
    		else{
    			reg double d=getDis(a.o,b.o);
    			reg double val=(sqr(a.r)+sqr(d)-sqr(b.r))/(2*d);
    			reg double h=sqrt(sqr(a.r)-sqr(val));
    			Point bas=a.o+(b.o-a.o)*(val/d);
    			Vector tmp=Vector(b.o.y-a.o.y,a.o.x-b.o.x)*(h/d);
    			Point p1=bas-tmp,p2=bas+tmp;
    			if(sgn(p2.x-p1.x)>0)
    				return p2;
    			else
    				return p1;
    		}
    	}
    }
    
    int n;
    Circle a[MAXN];
    
    int main(void){
    	scanf("%d",&n);
    	Point lef(0,0);
    	for(reg int i=1;i<=n;++i){
    		static int x,y,r;
    		scanf("%d%d%d",&x,&y,&r);
    		a[i].o=Point(x,y),a[i].r=r;
    		if(i==2)
    			lef=getPot(a[1],a[2]);
    		else if(i>2){
    			for(reg int j=1;j<i&&!isEmpty(lef);++j){
    				Point tmp=getPot(a[i],a[j]);
    				if(isEmpty(tmp)||tmp.x<=lef.x)
    					lef=tmp;
    			}
    			for(reg int j=1;j<=i&&!isEmpty(lef);++j)
    				if(!a[j].contain(lef))
    					lef=Point(nan(""),nan(""));
    		}
    		if(isEmpty(lef)){
    			printf("%d
    ",i);
    			return 0;
    		}
    	}
    	puts("NIE");
    	return 0;
    }
    
  • 相关阅读:
    Vue之常用语法
    Django之crm
    Django项目之客户
    前端、数据库、Django简单的练习
    Django的认证系统
    Django之form表单
    OpneCV 二值图像区域处理
    OpenCV图像处理中常用函数汇总(1)
    OpenCV3编程入门笔记(6)自我验证各函数程序代码
    OpenCV_轮廓的查找、表达、绘制、特性及匹配
  • 原文地址:https://www.cnblogs.com/Lu-Anlai/p/14873274.html
Copyright © 2011-2022 走看看