来写一篇题解。
关于并查集的基础内容请点击这里
题目传送门 洛谷P3598
思路
首先,我们可以将这些洞想象成一个点(就是球心)。题目询问的是小老鼠能否从奶酪最底下跑到奶酪最上面。意思就是,是否存在一个通道(连续很多个洞)从底面通向顶层。
这时候,就会自然而然的想到从底面的点开始搜索,能搜索到上面的点就输出Yes。(当然,搜索也是可以通过这一题的)
但我们应该思考:还有没有其他的方法? 当然有了不然就不会有下面的文字了
我们可以换一个角度想,对每一个能互相到达的点,我们都将它们染上一个颜色,最后只需要寻找顶部与底部有没有染成相同颜色的点就行了。
但我们肯定不会真的去“染色”,因为染色问题一般都用dfs(根据我现在的知识),用了染色,不就成了搜索了嘛。将思路转化一下,实际上,我们只需要对这些能互相联通的点打上相同的标记即可。
你想到了什么?自然而然的想到并查集啦。并查集对于能互相联通的点都会有一个相同的标记。这个时候问题就变成了求互联通的点的并查集,再对顶面和底面的点遍历,检查他们的祖先是否相同。
但是,这些点能否联通用什么来判断呢? 题目给了空间坐标系中的两点距离公式:
d
i
s
t
(
P
1
,
P
2
)
=
(
x
1
?
x
2
)
2
+
(
y
1
?
y
2
)
2
+
(
z
1
?
z
2
)
2
dist(P_1,P_2)=\sqrt{(x_1-x_2)^2+(y_1-y_2)^2+(z_1-z_2)^2}
dist(P1?,P2?)=(x1??x2?)2+(y1??y2?)2+(z1??z2?)2
?
实际上,我们只需判断这两个点的距离是否小于半径的两倍就可以啦。 当然,为了保证精度,我们只需比较它们的平方即可。即
d
i
s
t
(
P
1
,
P
2
)
2
=
(
x
1
?
x
2
)
2
+
(
y
1
?
y
2
)
2
+
(
z
1
?
z
2
)
2
dist (P_1,P_2)^2=(x_1-x_2)^2+(y_1-y_2)^2+(z_1-z_2)^2
dist(P1?,P2?)2=(x1??x2?)2+(y1??y2?)2+(z1??z2?)2
代码
下面上代码(有注释)
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<cstring>
#include<vector>
#define ll long long
#define uint unsighed int
#define ull unsighed long long
using namespace std;
const ll N=5e5+10;
const ll Z=1e9+7;
ll f[N];
ll f1[N],f2[N],cnt1,cnt2;
ll x[N],y[N],z[N];
void init(int n)
{
cnt1=cnt2=0;
for(int i=1;i<=n;++i) f[i]=i;
}
int find(int x)
{
if(x==f[x]) return x;
return f[x]=find(f[x]);
}
ll ksm(ll base,ll p)
{
ll ans=1;
while(p)
{
if(p&1) ans*=base;
base*=base;
p>>=1;
}
return ans;
}
int main()
{
ll T;cin>>T;
while(T--)
{
ll n,h,r;
cin>>n>>h>>r;
init(n);
for(ll i=1;i<=n;++i)
{
cin>>x[i]>>y[i]>>z[i];
if(z[i]+r>=h) f1[++cnt1]=i;
if(z[i]-r<=0) f2[++cnt2]=i;
}
for(ll i=1;i<=n;++i)
{
for(ll j=i+1;j<=n;++j)
{
ll fx=find(i);ll fy=find(j);
if(fx==fy) continue;
ll dist=ksm(x[i]-x[j],2)+ksm(y[i]-y[j],2)+ksm(z[i]-z[j],2);
ll rdist=r*r*4;
if(dist<=rdist) f[fx]=fy;
}
}
bool flag=0;
for(ll i=1;i<=cnt1;++i)
{
for(ll j=1;j<=cnt2;++j)
{
if(find(f1[i])==find(f2[j]))
{
flag=1;
break;
}
}
}
if(flag) cout<<"Yes"<<endl;
else cout<<"No"<<endl;
}
return 0;
}
类似的问题,也可以用并查集实现。
其他方法(搜索)
听说两种搜索方法都可以用。可以考虑链式前向星建图,再进行dfs和bfs等。就不放代码了。(因为我还没写)
代码中值得注意的地方
- 注意f数组的初始化
- 重新读入下一轮数据时,将f1,f2数组的计数变量重新初始化。
- 没了
|