zoukankan      html  css  js  c++  java
  • LOJ 6409. 「ICPC World Finals 2018」熊猫保护区

    官方题解,内附我不会的(O(nlogn))做法
    http://www.csc.kth.se/~austrin/icpc/finals2018solutions.pdf
    这题题意很简单.

    给你一个可以不凸的多边形,要求找一个点距离所有多边形的顶点最小距离最大.

    首先这个东西很像是半平面交,因为半平面交可以做

    给你一个可以不凸的多边形,要求找一个点距离所有多边形的边最小距离最大.

    然而直接上半平面交是搞不出来的.
    观察一下性质,显然最优取值点在某一点对的垂直平分线上.

    然后大力
    https://en.wikipedia.org/wiki/Voronoi_diagram
    一发.
    发现自己不会写找出维诺图的(O(nlogn))算法.
    这时候就有两种思考:
    1.乱搞,卡精度
    2.观察到数据范围很小,直接暴力半平面交找出维诺图的顶点.(然而我并没有想到这个)

    考虑乱搞,怎么乱搞呢,首先要利用结论,垂直平分线上的结论.
    那么我随机生成一个点,让他随机沿垂直平分线走一个随机的距离.实践证明,这样做很不理想.
    考虑抱精度,那么设一个步长,每次乘上一个alpha,搞一搞精度.
    还是不能过.

    因为点会走到维诺图外,所以只沿维诺图的某条边所在直线走,不一定可以走到维诺图的最优定点上.
    考虑随机一个角度,多随机几次,在那个角度上走步长.

    然后发现被近乎一条直线的多边形卡爆了,那么考虑再加一种策略,向一个随机顶点走.
    然后还是被卡爆了.

    考虑借鉴遗传算法的思想,在下一次的方向中借鉴上一次的较优解.
    然后就勉强可以水过.

    #include <bits/stdc++.h>
    
    using namespace std;
    template<class T>
    bool Chkmin(T &x,T y){
    	return x>y?(x=y,1):0;
    }
    template<class T>
    bool Chkmax(T &x,T y){
    	return x<y?(x=y,1):0;
    }
    template<class T>
    T sqr(T x){
    	return x*x;
    }
    typedef double ld;
    typedef long long i64;
    const ld INF=1e18;
    const ld EPS=1e-8;
    struct point{
    	ld x,y;
    	point operator +(const point &_) const{
    		return {x+_.x,y+_.y};
    	}
    	point operator -(const point &_) const{
    		return {x-_.x,y-_.y};
    	}
    	point operator /(const ld &_){
    		return {x/_,y/_};
    	}
    	point operator *(const ld &_){
    		return {x*_,y*_};
    	}
    	ld cross(const point &_) const{
    		return x*_.y-y*_.x;
    	}
    	ld dot(const point &_) const{
    		return x*_.x+y*_.y;
    	}
    	point rotate() const{
    		return {-y,x};
    	}
    	ld dis(){
    		return sqrt(sqr(x)+sqr(y));
    	}
    };
    ostream& operator <<(ostream & os,const point &_){
    	return os<<"{"<<_.x<<","<<_.y<<"}";
    }
    struct segment{
    	point s,t;
    	bool strict_cross(const segment &_) const{
    		return (t-s).cross(_.s-s)*(t-s).cross(_.t-s)<0&&(_.t-_.s).cross(s-_.s)*(_.t-_.s).cross(t-_.s)<0;
    	}
    	bool operator &(const segment &_) const{
    		//cerr<<"&"<<s<<" "<<t<<",,"<<_.s<<" "<<_.t<<endl;
    		return strict_cross(_);
    	}
    };
    struct polygon{
    	vector<point> data;
    	size_t next(size_t val) const{
    		return val==size()-1?0:val+1;
    	}
    	size_t size() const{
    		return data.size();
    	}
    	segment edge(int x) const{
    		return segment({data[x],data[next(x)]});
    	}
    	point& operator [](const size_t &x){
    		return data[x];
    	}
    };
    bool in(const point &test,const polygon &con){
    	static const point add({42348.23712341,34435.61431236});
    	int counter=0;
    	for (int i=0; i<con.size(); ++i)
    		if (segment({test,test+add})&con.edge(i)) ++counter;
    	return counter&1;
    }
    point neareast(const point &test,polygon &con){
    	ld nowdis=(test-con[0]).dis();
    	point rvalue=con[0];
    	for (size_t i=1; i<con.size(); ++i)
    		if (Chkmin(nowdis,(test-con[i]).dis())) rvalue=con[i];
    	return rvalue;
    }
    const int C=10000;
    const int T=1000;
    point rec[T];
    bool b[T];
    ld Rand(){
    	return (((i64)rand()<<31)+rand());
    }
    int mxx,mix,mxy,miy;
    int RandIntX(){
    	return rand()%(mxx-mix+1)+mix;
    }
    int RandIntY(){
    	return rand()%(mxy-miy+1)+miy;
    }
    polygon con;
    point trans(ld x,point y){
    	return y*(x/y.dis());
    }
    point pp(){
    	int x,y;
    	do{
    		x=rand()%con.size();
    		y=rand()%con.size();
    	}while (x==y);
    	return (con[x]-con[y]).rotate();
    }
    point conclude(int TT){
    	point x({RandIntX(),RandIntY()});
    	//bug here
    	bool fi=1;
    	for (ld step=C/2; step>EPS; step*=0.8){
    		//cerr<<x<<" "<<step<<endl;
    		rec[1]=x+trans(step,x-rec[0]);
    		rec[0]=x;
    		if (fi)
    			for (int i=2,t; i<TT; ++i){
    				if ((t=rand())&1){
    					rec[i]=x+trans(step,pp());//optimize
    				}
    				else if ((t>>1)&1) rec[i]=x+trans(step,(con[rand()%con.size()]-x));
    				else{
    					ld alpha=Rand();
    					rec[i]=x+point({step*cos(alpha),step*sin(alpha)});
    				}
    			}
    		else
    			for (int i=2,t; i<TT; ++i)
    				if ((t=rand())&1){
    					ld alpha=Rand();
    					rec[i]=x+point({step*cos(alpha),step*sin(alpha)});
    				}
    				else if ((t>>1)&1) rec[i]=x+trans(step,(con[rand()%con.size()]-x));
    				else{
    					rec[i]=x+trans(step,pp());//optimize
    				}
    		int counter=0;
    		for (int i=0; i<TT; ++i)
    			if (in(rec[i],con)){//bug
    				//cerr<<"inint"<<i<<" "<<rec[i]<<endl;
    				if (fi){
    					//cerr<<x<<endl;
    					fi=0;
    					step=C/2;
    				}
    				b[i]=1;
    				++counter;
    			}
    			else b[i]=0;
    		//getchar();
    		//exit(0);
    		if (counter){
    			ld nowdis=-1;
    			for (int i=0; i<TT; ++i)
    				if (b[i]&&Chkmax(nowdis,(rec[i]-neareast(rec[i],con)).dis())){
    					//cerr<<"UPD"<<nowdis<<endl;
    					x=rec[i];
    				}
    		}
    		else{
    			ld nowdis=INF;
    			for (int i=0; i<TT; ++i)
    				if (Chkmin(nowdis,(rec[i]-neareast(rec[i],con)).dis())){
    					x=rec[i];
    				}
    		}
    	}
    	return x;
    }
    int main(){
    	int n;
    	cin>>n;
    	con.data.resize(n);
    	mxx=-10000;
    	mix=10000;
    	mxy=-10000;
    	miy=10000;
    	for (int i=0; i<n; ++i){
    		int x,y;
    		cin>>x>>y;
    		con[i]=point({x,y});
    		mix=min(mix,x);
    		mxx=max(mxx,x);
    		miy=min(miy,y);
    		mxy=max(mxy,y);
    	}
    	int TIME=clock();
    	ld ans=0;
    	while (clock()-TIME<CLOCKS_PER_SEC){
    		int p=rand()%15+5;
    		//cerr<<"P"<<p<<endl;
    		point tmp=conclude(p);
    		ans=max(ans,(tmp-neareast(tmp,con)).dis());
    	}
    	while (clock()-TIME<CLOCKS_PER_SEC*1.5){
    		int p=rand()%200;
    		if (p>100) p=rand()%200;
    		if (p>100) p=rand()%200;
    		if (p>100) p=rand()%200;
    		if (p>100) p=rand()%200;
    		p=max(p,30);
    		point tmp=conclude(p);
    		ans=max(ans,(tmp-neareast(tmp,con)).dis());
    		//cerr<<"amns"<<ans<<endl;
    	}
    	cout<<fixed<<setprecision(10)<<ans;
    }
    

    考虑正经算法.
    半平面交怎么求维诺图呢?
    把某一个顶点作为线段端点的垂直平分线搞出来,然后暴力半平面交,最后的点集就是维诺图上在该点附近的顶点.
    结论:维诺图的顶点数和边数都为(O(n))级别.
    注意维诺图的边和多边形的交点也有可能是答案.
    复杂度(O(n^2logn))

    #pragma GCC optimize("Ofast")
    #pragma GCC optimize("inline")
    #pragma GCC optmize(3)
    #include <bits/stdc++.h>
    using namespace std;
    template<class T>
    bool Chkmin(T &x,T y){
    	return x>y?(x=y,1):0;
    }
    template<class T>
    bool Chkmax(T &x,T y){
    	return x<y?(x=y,1):0;
    }
    template<class T>
    T sqr(T x){
    	return x*x;
    }
    typedef double ld;
    typedef long long i64;
    const ld INF=1e18;
    const ld EPS=1e-9;
    struct point{
    	ld x,y;
    	point operator +(const point &_) const{
    		return {x+_.x,y+_.y};
    	}
    	point operator -(const point &_) const{
    		return {x-_.x,y-_.y};
    	}
    	point operator /(const ld &_) const{
    		return {x/_,y/_};
    	}
    	point operator *(const ld &_) const{
    		return {x*_,y*_};
    	}
    	ld cross(const point &_) const{
    		return x*_.y-y*_.x;
    	}
    	ld dot(const point &_) const{
    		return x*_.x+y*_.y;
    	}
    	bool operator ==(const point &_) const{
    		return x==_.x&&y==_.y;
    	}
    	bool operator !=(const point &_) const{
    		return x!=_.x||y!=_.y;
    	}
    	point rotate() const{
    		return {-y,x};
    	}
    	ld dis() const{
    		return sqrt(sqr(x)+sqr(y));
    	}
    	ld sqrdis() const{
    		return x*x+y*y;
    	}
    	point makelong(ld len=3e4) const{
    		ld t=len/(dis());
    		return (*this)*t;
    	}
    };
    ld atan2(const point &x){
    	return atan2(x.y,x.x);
    }
    ostream& operator <<(ostream & os,const point &_){
    	return os<<"{"<<_.x<<","<<_.y<<"}";
    }
    struct segment{
    	point s,t;
    	bool strict_cross(const segment &_) const{
    		return (t-s).cross(_.s-s)*(t-s).cross(_.t-s)<0&&(_.t-_.s).cross(s-_.s)*(_.t-_.s).cross(t-_.s)<0;
    	}
    	bool operator &(const segment &_) const{
    		//cerr<<"&"<<s<<" "<<t<<",,"<<_.s<<" "<<_.t<<endl;
    		return strict_cross(_);
    	}
    	bool onright(const point &_) const{
    		return (_-s).cross(t-s)>0;
    	}
    	point generator(const ld &_) const{
    		return s+(t-s)*_;
    	}
    	point intersect(const segment &_) const{
    		ld t1=(t-s).cross(_.t-_.s);
    		ld t2=(_.t-_.s).cross(s-_.s);
    		return generator(t2/t1);
    	}
    	bool on(const point &_) const{
    		//cerr<<(_-s).dot(_-t)<<" "<<fabs((_-s).cross(_-t))<<endl;
    		return (_-s).dot(_-t)<0&&fabs((_-s).cross(_-t))<EPS;
    	}
    	bool line_on(const point &_) const{
    		//cerr<<(_-s).dot(_-t)<<" "<<fabs((_-s).cross(_-t))<<endl;
    		return (_-s).dot(_-t)<0;
    	}
    };
    ld atan2(const segment &x){
    	return atan2(x.t-x.s);
    }
    ld ans;
    struct polygon:vector<point>{
    	size_t next(size_t val) const{
    		return val==size()-1?0:val+1;
    	}
    	segment edge(int x) const{
    		return segment({(*this)[x],(*this)[next(x)]});
    	}
    	ld neareast(const point &p) const{
    		//cerr<<"ne"<<p<<endl;
    		ld ret=INF;
    		ld c=ans*ans;
    		for (auto i:*this)
    			if ((ret=min(ret,(i-p).sqrdis()))<=c) break;
    		//if (sqrt(ret)>30000) cerr<<"SUCC"<<p<<" "<<sqrt(ret)<<endl;
    		return sqrt(ret);
    	}
    	bool in(const point &p) const{
    		for (size_t i=0; i<this->size(); ++i){
    			auto j=edge(i);
    			if (j.on(p)) return 1;
    		}
    		static const point v={43333.3435499546546L,74354.4534534512434L};
    		int ret=0;
    		for (size_t i=0; i<this->size(); ++i){
    			auto j=edge(i);
    			ret+=segment{p,p+v}&j;
    		}
    		return ret&1;
    	}
    };
    typedef vector<segment> vs;
    polygon con;
    struct Segment:segment{
    	ld tt;
    	Segment(){
    	}
    	Segment(const segment &x):segment(x){
    		tt=atan2(x);
    	}
    };
    polygon halfplaneintersect(vs v){
    	vector<Segment> vp(v.begin(),v.end());
    	stable_sort(vp.begin(),vp.end(),[](const Segment &x,const Segment &y)->bool{
    			if (x.tt+EPS<y.tt) return 1;
    			if (y.tt+EPS<x.tt) return 0;
    			return x.onright(y.s);
    		});
    	//for (auto i:v) cerr<<i.s<<" "<<i.t<<endl;
    	//exit(0);
    	static const int N=2005;
    	static Segment q[N];
    	static point p[N];
    	int l=0,r=-1;
    	for (int i=0; i<vp.size(); ++i){
    		//cerr<<"Line: "<<"	"<<v[i].s<<" "<<v[i].t<<" "<<atan2(v[i])<<endl;
    		while (l<r&&vp[i].onright(p[r])) --r;
    		while (l<r&&vp[i].onright(p[l+1])) ++l;
    		if (l<=r&&vp[i].tt<q[r].tt+EPS) continue;
    		q[++r]=vp[i];
    		if (l<r){
    			//cerr<<"intersect 	 	 	"<<l<<" "<<r<<" "<<p[r]<<endl;
    			p[r]=q[r-1].intersect(q[r]);
    		}
    		//cerr<<"this"<<l<<" "<<r<<endl;
    	}
    	while (l<r&&q[l].onright(p[r])) --r;
    	while (l<r&&q[r].onright(p[l+1])) ++l;
    	//cerr<<"atlast 	"<<l<<" "<<r<<endl;
    	p[l]=q[l].intersect(q[r]);
    	polygon ret;
    	ret.assign(p+l,p+r+1);
    	for (int i=l; i<=r; ++i){
    		segment t1=q[i];
    		for (int j=0; j<con.size(); ++j){
    			segment t2=con.edge(j);
    			point tmp=t1.intersect(t2);
    			//cerr<<"tmp"<<tmp<<" "<<t1.s<<" "<<t1.t<<" "<<t1.on(tmp)<<endl;
    				//cerr<<"init"<<endl;
    			if (t2.line_on(tmp))
    				ans=max(ans,con.neareast(tmp));
    		}
    	}
    	//for (auto i:ret) cerr<<i<<"|";
    	//cerr<<endl;
    	return ret;
    }
    segment perpendicular_bisector(point x,point y){
    	point a=(x+y)/2,b=(y-x).rotate().makelong();
    	return {a-b,a+b};
    }
    void solve(polygon result){
    	//cerr<<"RE"<<result.size()<<endl;
    	for (auto i:result){
    		//cerr<<i<<endl;
    		if (con.in(i)){
    			//cerr<<"I"<<i<<endl;
    			ans=max(ans,con.neareast(i));
    		}
    	}
    }
    void test(){
    	segment a2({{0,0},{1,1}});
    	segment a1({{1,0},{-1,0}});
    	cerr<<a1.intersect(a2)<<endl;
    }
    int main(){
    	//test();
    	int n;
    	cin>>n;
    	con.resize(n);
    	for (int i=0; i<n; ++i){
    		int x,y;
    		cin>>x>>y;
    		con[i]={x,y};
    	}
    	for (auto i:con){
    		//cerr<<"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"<<i<<endl;
    		vs tmp;
    		for (auto j:con)
    			if (i!=j){
    				tmp.push_back(perpendicular_bisector(i,j));
    				//cerr<<tmp.back().s<<" "<<tmp.back().t<<" "<<j<<endl;
    			}
    		solve(
    		halfplaneintersect(tmp)
    				);
    		//return 0;
    	}
    	cout<<fixed<<setprecision(10)<<ans;
    }
    
  • 相关阅读:
    c# excel sheep 导出
    C# 导出 excel 复杂格式 html导出
    C# 导出CSV功能记录下
    怎样查看修改sqlserver数据库的编码格式
    entity framework如何控制并发
    IT技术 | 让程序员抓狂的排序算法教学视频
    关于高性能的那点事
    论C#逼格手册
    numpy.loadtxt用法
    numpy中np.c_和np.r_
  • 原文地址:https://www.cnblogs.com/Yuhuger/p/10738412.html
Copyright © 2011-2022 走看看