【BZOJ3007】拯救小云公主 二分+几何+对偶图

【BZOJ3007】拯救小云公主

Description

    英雄又即将踏上拯救公主的道路……
    这次的拯救目标是——爱和正义的小云公主。
    英雄来到boss的洞穴门口,他一下子就懵了,因为面前不只是一只boss,而是上千只boss。当英雄意识到自己还是等级1的时候,他明白这就是一个不可能完成的任务。
    但他不死心,他在想,能不能避开boss去拯救公主呢,嘻嘻。
    Boss的洞穴可以看成一个矩形,英雄在左下角(1,1),公主在右上角(row,line)。英雄为了避开boss,当然是离boss距离越远越好了,所以英雄决定找一条路径使到距离boss的最短距离最远。
Ps:英雄走的方向是任意的。
    你可以帮帮他吗?
    当英雄找到了美丽漂亮的小云公主,立刻就被boss包围了!!!英雄缓闭双眼,举手轻挥,白光一闪后使用了回城卷轴,回到了城堡,但只有小云公主回去了……因为英雄忘了进入回城的法阵了。

Input

    第一行,输入三个整数,n表示boss的数目,row,line表示矩形的大小;
    接下来n行,每行分别两个整数表示boss的位置坐标。

Output

    输出一个小数,表示英雄的路径离boss的最远距离,精确到小数点后两位。

Sample Input

1 3 3
2 2
输出样例1:
1.00
输入样例2:
1 3 3
3 1
输出样例2:
2.00

Sample Output

HINT

100%数据,n<=3000;

题解:离所有boss的最远距离为x可以转化为将所有boss看成半径为x的圆,且存在一条从左下角走到右上角的路径与所有圆不相交,即左下角和右上角在一个域中。这个距离显然是可以二分的。并且,左下角和右下角在一个域中等价于在对偶图中,左上角和右上角不连通。于是用并查集判断即可。

#include <cstdio>
#include <iostream>
#include <cstring>
#include <cmath>
const int maxn=3010;
int n;
int f[maxn];
double mx,my;
double x[maxn],y[maxn];
inline int rd()
{
	int ret=0,f=1;	char gc=getchar();
	while(gc<'0'||gc>'9')	{if(gc=='-')f=-f;	gc=getchar();}
	while(gc>='0'&&gc<='9')	ret=ret*10+gc-'0',gc=getchar();
	return ret*f;
}
double dis2(int a,int b)
{
	return (x[a]-x[b])*(x[a]-x[b])+(y[a]-y[b])*(y[a]-y[b]);
}
int find(int x)
{
	return (f[x]==x)?x:(f[x]=find(f[x]));
}
void uni(int a,int b)
{
	if(find(a)!=find(b))	f[f[a]]=f[b];
}
bool check(double r)
{
	int i,j;
	for(i=1;i<=n+2;i++)	f[i]=i;	
	for(i=1;i<=n;i++)
	{
		if(x[i]-r<=1||y[i]+r>=my)	uni(i,n+1);
		if(x[i]+r>=mx||y[i]-r<=1)	uni(i,n+2);
	}
	for(i=1;i<=n;i++)	for(j=i+1;j<=n;j++)	if(dis2(i,j)<=4*r*r)	uni(i,j);
	return find(n+1)!=find(n+2);
}
int main()
{
	n=rd(),mx=rd(),my=rd();
	int i;
	for(i=1;i<=n;i++)	x[i]=rd(),y[i]=rd();
	double l=0,r=mx+my,mid;
	while(r-l>1e-4)
	{
		mid=(l+r)/2;
		if(check(mid))	l=mid;
		else	r=mid;
	}
	printf("%.2lf",l);
	return 0;
}
原文地址:https://www.cnblogs.com/CQzhangyu/p/7391457.html