P2498 [SDOI2012] 拯救小云公主

[SDOI2012] 拯救小云公主

题目描述

英雄又即将踏上拯救公主的道路……

这次的拯救目标是——爱和正义的小云公主。

英雄来到boss的洞穴门口,他一下子就懵了,因为面前不只是一只boss,而是上千只boss。当英雄意识到自己还是等级1的时候,他明白这就是一个不可能完成的任务。

但他不死心,他在想,能不能避开boss去拯救公主呢,嘻嘻。

Boss的洞穴可以看成一个矩形,英雄在左下角(1,1),公主在右上角(row,line)。英雄为了避开boss,当然是离boss距离越远越好了,所以英雄决定找一条路径使到距离boss的最短距离最远。

Ps:英雄走的方向是任意的,但是不能走出矩形的范围。即英雄可以到达矩形范围内的任意一个点(没有必要是整点)

你可以帮帮他吗?

当英雄找到了美丽漂亮的小云公主,立刻就被boss包围了!!!英雄缓闭双眼,举手轻挥,白光一闪后使用了回城卷轴,回到了城堡,但只有小云公主回去了……因为英雄忘了进入回城的法阵了。

输入格式

第一行,输入三个整数,n表示boss的数目,row,line表示矩形的大小;

接下来n行,每行分别两个整数表示boss的位置坐标。

输出格式

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

样例 #1

样例输入 #1

1 3 3
2 2

样例输出 #1

1.00

样例 #2

样例输入 #2

1 3 3
3 1

样例输出 #2

2.00

提示

数据范围:

20%数据,boss坐标范围小于等于50;

60%数据,n<=1500;

100%数据,n<=3000;

分析

二分答案r之后,就有n 个以给定点为圆心,r 为半径的圆,表示人不能经过这些圆。我们发现当r太大时,人无法到达,故使用并查集判断是否可行,若矩形的下、右部分,通过若干个圆,能到达矩形的左、上部分,其中相邻的图形之间相交,就类似于把平面“割”开了,则不能到达

代码

#include 
using namespace std;
const int M=3010;
#define pow_2(x) ((x)*(x))
int r,c,n;
int bx[M],by[M],f[M];
int find(int x){return f[x]==x?x:f[x]=find(f[x]);}
void init(int n){for (int i=0;i<=n;i++) f[i]=i;}
void merge(int i,int j){
	int x=find(i),y=find(j);
	if (x!=y) f[x]=y;
}
void read(){
	cin>>n>>r>>c;
	for (int i=1;i<=n;i++) cin>>bx[i]>>by[i];
}
int dis(int i,int j){
	return pow_2(bx[i]-bx[j])+pow_2(by[i]-by[j]);
}
bool check(double r){
	init(n+1);
	for (int i=1;i<=n;i++){
		for (int j=1;j<i;j++)
		    if (dis(i,j)<=pow_2(2*r)) merge(i,j);
		if (bx[i]-r<=1 or by[i]+r>=c) merge(i,0);
		if (bx[i]+r>=::r or by[i]-r<=1) merge(i,n+1);
	}
	return find(0)!=find(n+1);
}
void solve(){
	double l=0,r=min(::r,c),ans;
	while(r-l>=1e-4){
		double mid=(l+r)/2;
		if(check(mid)) ans=mid,l=mid;
		else r=mid;
	}
	printf("%.2f",ans);
}
signed main() {
	read();
	solve();
	return 0;
}

代码分析

for (int i=1;i<=n;i++){
		for (int j=1;j<i;j++)
		    if (dis(i,j)<=pow_2(2*r)) merge(i,j);
		if (bx[i]-r<=1 or by[i]+r>=c) merge(i,0);
		if (bx[i]+r>=::r or by[i]-r<=1) merge(i,n+1);
	}

三种情况:
1.若与其他点联通:与其他点合并
2.与边界联通:与特殊点合并

你可能感兴趣的:(算法,数据结构)