【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
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; }
| 欢迎来原网站坐坐! >原文链接<