题意
有一条走廊可以视为二维平面上由
y
=
0
y=0
y=0与
y
=
w
y=w
y=w两条直线所框定的白色区域,中间有n个的报警传感器位于墙壁上或者走廊中。伊森在模型中可以视为具有一定半径的圆,位于走廊的最左侧
x
=
−
∞
x=−∞
x=−∞,想穿过走廊到达最右侧
x
=
+
∞
x=+∞
x=+∞。求该圆可以具有的最大半径。
n
≤
1000
,
0
≤
w
≤
1
0
5
,
−
1
0
5
≤
x
,
y
≤
1
0
5
n \le 1000,0 \le w \le 10^5, -10^5 \le x,y \le10^5
n≤1000,0≤w≤105,−105≤x,y≤105
题解
显然可以二分答案,问题在于如何验证答案。
- 假设二分的圆的半径为R,如果直接判断该圆通过走廊,那么验证是比较困难的;
- 换一个思路,如果将所有传感器的半径增加R(同时走廊间距减小2R),判断一个点能否通过走廊,就比较容易了。
此时只需用并查集维护一下连接起来的传感器的集合,每个集合维护一下上界和下界,判断一下是否有将路堵死的情况即可。每次判断复杂度为 O ( n 2 α ) O(n^2\alpha) O(n2α),总复杂度为 O ( T ∗ α n 2 l o g 2 ( w ) ) O(T*\alpha n^2log_2(w)) O(T∗αn2log2(w))
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1200;
int x[N],y[N],r[N],n,w;
double dx[N][N];
int f[N];
double u[N],d[N];
int ff(int x){
if(f[x]==x)return x;
return f[x]=ff(f[x]);
}
bool check(double rx){
for(int i=0;i<n;i++){
u[i]=y[i]+r[i]+rx;
d[i]=y[i]-r[i]-rx;
f[i]=i;
for(int j=0;j<i;j++){
if(r[i]+r[j]+rx*2>dx[i][j]){
int fi=ff(i),fj=ff(j);
f[fi]=fj;
u[fj]=max(u[fj],u[fi]);
d[fj]=min(d[fj],d[fi]);
}
}
}
for(int i=0;i<n;i++)
if(u[i]>w-rx && d[i]<rx)return 0;
return 1;
}
int main(){
ios::sync_with_stdio(0);
int T;
cin>>T;
while(T--){
cin>>w>>n;
for(int i=0;i<n;i++){
cin>>x[i]>>y[i]>>r[i];
for(int j=0;j<i;j++)
dx[i][j]=sqrt(ll(x[i]-x[j])*(x[i]-x[j])+ll(y[i]-y[j])*(y[i]-y[j]));
}
double l=0,r=w/2.0;
while(r-l>=1e-8){
double m=(l+r)/2;
if(check(m))l=m;
else r=m;
}
printf("%.8f\n",r);
}
return 0;
}