標籤:style blog http color os io for art
題意:一個NxN的網格地板,有一些雷射束從天花板垂直射向地面的某個網格,一個圓要安全地從左走到右,不碰到上邊界,下邊界以及雷射束,問這個圓的直徑最大能達到多大。
分析:可以二分直徑,關鍵在check函數的寫法。可以講這個圓縮成一個點,把圓的直徑轉化為雷射的掃描範圍,當雷射範圍完全堵死一條通道的時候,這個直徑則是不可行的。怎樣判斷是否堵死一條通道了呢。每次check(dis)的時候,枚舉雷射束對,如果雷射束之間距離小於dis,那麼它們兩個之間建一條邊。還要注意處理邊界,如果雷射束範圍與上邊界或下邊界相交,那麼邊界與這個雷射束也建一條邊。最後從一個邊界dfs到另一個邊界,如果能夠dfs到,那麼說明此時通道被堵死,此dis是不行的。繼續二分就能得到答案。
代碼:
#include <iostream>#include <cstdio>#include <cstring>#include <cmath>#include <algorithm>#include <string>#include <vector>using namespace std;#define N 5007struct node{ int x,y;}p[N];double d[N][N];vector<int> G[N];int n,L;int vis[N],S,E;double dis(node ka,node kb){ return (double)sqrt((ka.x-kb.x)*(ka.x-kb.x)+(ka.y-kb.y)*(ka.y-kb.y));}int dfs(int u,int fa){ if(u == E) return 1; vis[u] = 1; for(int i=0;i<G[u].size();i++) { int v = G[u][i]; if(v == fa) continue; if(vis[v]) continue; if(dfs(v,u)) return 1; } return 0;}bool check(double D){ S = 0; E = L+1; for(int i=0;i<=L+1;i++) G[i].clear(); for(int i=1;i<=L;i++) { if(p[i].y < D) G[S].push_back(i); if(p[i].y + D > n) G[i].push_back(E); for(int j=i+1;j<=L;j++) { if(d[i][j] < D) { G[i].push_back(j); G[j].push_back(i); } } } memset(vis,0,sizeof(vis)); if(dfs(S,-1)) return 0; return 1;}int main(){ int i,j; while(scanf("%d%d",&n,&L)!=EOF && n) { for(i=1;i<=L;i++) scanf("%d%d",&p[i].x,&p[i].y); for(i=1;i<=L;i++) for(j=i+1;j<=L;j++) d[i][j] = dis(p[i],p[j]); double low = eps; double high = n; while(low+eps < high) { double mid = (low+high)/2.0; if(check(mid)) low = mid; else high = mid; } printf("%.3lf\n",low); } return 0;}View Code