这个题可以看作是图上有很多圆形禁区,如果直接考虑是否可以从左边走到右边似乎有些无从下手,其实可以这样考虑,把整个方形区域看作水面,把禁区看作小岛,如果可以从上边沿着这些小岛走到下边的话,就说明方形区域被禁区整个隔断了,那么就不能从左边走到右边,这样一来DFS即可。(思路来自紫书)
至于最北的位置,只需要在DFS的时候计算当前禁区与左右边缘的下交点,不断更新坐标值即可。因为我们是从与上边缘相交的禁区开始DFS的,所以从上边缘到当前禁区与左右边缘的下交点中间的区域都是无法通过的。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1000 + 5;
const double w = 1000.0;
int n;
int x[maxn],y[maxn],r[maxn];
int G[maxn][maxn];
int visited[maxn];
int up[maxn], down[maxn];
double le,ri;
int connect(int i,int j){
return pow(r[i]+r[j],2) >= pow(x[i]-x[j],2) + pow(y[i]-y[j],2);
}
void calCross(int i){
if(x[i] - r[i] <= 0) le = min(le,y[i] - sqrt(r[i]*r[i] - x[i]*x[i]));
if(x[i] + r[i] >= 1000) ri = min(ri,y[i] - sqrt(r[i]*r[i] - (w-x[i])*(w-x[i])));
}
int DFS(int i){
if(down[i]) return 1;
visited[i] = 1;
int mark = 0;
for(int j = 0;j < n;j++){
if(G[i][j] && !visited[j]){ // j !!!
mark = DFS(j);
if(mark) break;
}
}
calCross(i);
return mark;
}
int main()
{
// freopen("data.in","r",stdin);
// freopen("data.out","w",stdout);
while(scanf("%d",&n) != EOF){
memset(visited,0,sizeof(visited));
memset(G,0,sizeof(G));
memset(up,0,sizeof(up));
memset(down,0,sizeof(down));
le = ri = w;
for(int i = 0;i < n;i++){
scanf("%d%d%d",&x[i],&y[i],&r[i]);
}
for(int i = 0;i < n;i++){
if(y[i] + r[i] >= 1000) up[i] = 1;
if(y[i] - r[i] <= 0) down[i] = 1;
for(int j = i+1;j < n;j++){
if(connect(i,j)) G[i][j] = G[j][i] = 1;
}
}
int mark = 0;
for(int i = 0;i < n;i++){
if(up[i] && !visited[i]){
mark = DFS(i);
if(mark) break;
}
}
if(!mark) printf("0.00 %.2f 1000.00 %.2f\n",le,ri);
else puts("IMPOSSIBLE");
}
return 0;
}