[UVA11853]Paintball
算法入门经典第6章6-22(P175)
题目大意:有一个1000*1000的正方形战场,西南角坐标(0,0),西北角坐标(0,1000),有n个敌人,每个敌人处在(xi,yi),攻击范围为ri,要避开他们的攻击范围,求从最左边出发的最北边出发点及右边的最北边到达点。
试题分析:我们先判断是否能有方案,如何判断?将相交圆的圆心相连,看从交上边界的圆出发是否能到达与下边界相交的圆。然后再这个过程中如果看到与左/右边界相交的圆那么更新答案就好了。
#include<iostream> #include<cstring> #include<cstdio> #include<vector> #include<cmath> #include<queue> #include<stack> #include<algorithm> using namespace std; inline int read(){ int x=0,f=1;char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; } const int MAXN=2001; const int INF=999999; int N,M; vector<int> vec[MAXN]; struct data{ double x,y,r; }cir[MAXN]; bool vis[MAXN]; double ansl,ansr; bool flag=false; double dis(int a,int b){ double x=cir[a].x-cir[b].x; double y=cir[a].y-cir[b].y; return sqrt(x*x+y*y); } bool DFS(int x){ if(vis[x]||flag) return false; vis[x]=true; if(cir[x].x-cir[x].r<=0){ flag=true; return true; } if (cir[x].y<=cir[x].r) ansl=min(ansl,cir[x].x-sqrt(cir[x].r*cir[x].r-cir[x].y*cir[x].y)); if (cir[x].y+cir[x].r>=1000) { double k=1000-cir[x].y; ansr=min(ansr,cir[x].x-sqrt(cir[x].r*cir[x].r-k*k)); } for(int i=0;i<vec[x].size();i++) DFS(vec[x][i]); return false; } int main(){ while(scanf("%d",&N)!=EOF){ for(int i=1;i<=N;i++) vec[i].clear(); ansl=ansr=1000.0; flag=false; for(int i=1;i<=N;i++){ scanf("%lf",&cir[i].y); scanf("%lf",&cir[i].x); scanf("%lf",&cir[i].r); } for(int i=1;i<N;i++){ for(int j=i+1;j<=N;j++){ if(dis(i,j)<=cir[i].r+cir[j].r){ vec[i].push_back(j); vec[j].push_back(i); } } } memset(vis,false,sizeof(vis)); for(int i=1;i<=N;i++) if(cir[i].r+cir[i].x>=1000) if(DFS(i)) break; if(flag) printf("IMPOSSIBLE "); else printf("%.2f %.2f %.2f %.2f ",0.0,ansl,1000.0,ansr); } }