题目大意
在 $ 100*100 $ 的平面空间内,一个半径为$r$的小球要从平面底部$(y=0)$移动到平面顶部$(y=100)$
空间内有$n$个点,第$i$个点的坐标为$(x_i,y_i)$,小球在移动的过程中不能碰到这些点以及左右的边界,询问对于给定的球半径$r$与点集,小球能否从底部走到顶部。
解题思路
可以将球看作点来处理,那么可以将球看作点,将空间内的点看作半径为$r$的区域,未覆盖到的区域及小球能走的区域。小球能否从底部走到顶部即判断这些区域是否能将左边界和右边界连通起来。
即可将左右边界看作起点和终点,将与左右边界的距离小于$r$的点、点间距小于直径的点,建立无向边。最后判断起点和终点是否联通,再特判下$n=0$且$r>=50$的情况即可。
判断是否联通可用并察集解决,时间复杂度$O(n^2)$
AC代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
| #include<bits/stdc++.h> using namespace std; const int maxn = 1005; int T,n; double r,eps=1e-9; struct point{ double x,y; }p[2*maxn]; double dis(int a,int b) { double sx=p[a].x-p[b].x; double sy=p[a].y-p[b].y; return sqrt(sx*sx+sy*sy); }
const int MAXN=maxn; int par[MAXN]; int renk[MAXN]; void init_union_find(int V) { for(int i=0;i<=V;i++) { par[i]=i; renk[i]=0; } }
int find(int x) { if(par[x]==x) { return x; } else { return par[x] = find(par[x]); } }
void unite(int x,int y) { x=find(x); y=find(y); if(x==y)return; if(renk[x]<renk[y]) { par[x]=y; } else { par[y]=x; if(renk[x]==renk[y])renk[x]++; } }
bool same(int x,int y) { return find(x) == find(y); }
int main() { cin>>T; while(T--) { cin>>n>>r; init_union_find(n+1); int s=0,t=n+1; if(r>=50) { unite(s,t); } double x,y; for(int i=1;i<=n;i++) { cin>>x>>y; p[i].x=x; p[i].y=y; if(2*r-x>0 || fabs(2*r-x)<eps) { unite(s,i); }
if(x+2*r-100>0 || fabs(x+2*r-100)<eps ) { unite(t,i); } for(int j=i-1;j>=1;j--) { if(dis(i,j)-2*r<0 || fabs(dis(i,j)-2*r)<eps) { unite(i,j); } } }
if(same(s,t)) { cout<<"No"<<endl; } else cout<<"Yes"<<endl; } }
|
Last updated: