[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 <bits/stdc++.h>
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.与边界联通:与特殊点合并