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;
}
原文地址:https://www.cnblogs.com/Yuhuger/p/10738412.html