原题链接

题目大意

在 $ 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]; //树的高度 rank取名会报错
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]);
}
}
//合并x与y所属集合
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]++;
}
}
// 判断x与y是否属于同一个集合
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;
}
}