并查集是非常常用的一种数据结构,用于把数据按照规则整理成集合,集合最终呈现为树状结构,以根节点作为不同集合的区分标志,实现方面主要涉及查找和合并,代码如下
//查找
int find(int x)
{
int r=x;
while(pre[r]!=r)
r=pre[r];//找到他的前导结点
int i=x,j;
while(i!=r)//路径压缩算法
{
j=pre[i];//记录x的前导结点
pre[i]=r;//将i的前导结点设置为r根节点
i=j;
}
return r;
}
//合并
void UnionSet(int x, int y){
int xp = find(x);
int yp = find(y);
pre[xp] = yp;
}
代码不难理解,其目的就是为了构建一个森林将离散的数据变成集合,这里我们学习如何用并查集判断图的环路,并查集判断图的方法是按边合并集合,对于图,我们可以做一个邻接矩阵或者直接按边来遍历合并集合(每个边遍历一次),当遍历一条边出现两个顶点早就处于同一个集合时即可判定有环路。
以51Nod-1416为例:
福克斯在玩一款手机解迷游戏,这个游戏叫做”两点”。基础级别的时候是在一个n×m单元上玩的。像这样:
每一个单元有包含一个有色点。我们将用不同的大写字母来表示不同的颜色。
这个游戏的关键是要找出一个包含同一颜色的环。看上图中4个蓝点,形成了一个环。一般的,我们将一个序列 d1,d2,...,dk 看成一个环,当且仅当它符合下列条件时:
1. 这k个点不一样,即当 i≠j时, di 和 dj不同。
2. k至少是4。
3. 所有的点是同一种颜色。
4. 对于所有的 1≤i≤k-1: di 和 di+1 是相邻的。还有 dk 和 d1 也应该相邻。单元 x 和单元 y 是相邻的当且仅当他们有公共边。
当给出一幅格点时,请确定里面是否有环。
代码:
#include <bits/stdc++.h>
using namespace std;
int pre[2505];
int find(int x)
{
int r=x;
while(pre[r]!=r)
r=pre[r];//找到他的前导结点
int i=x,j;
while(i!=r)//路径压缩算法
{
j=pre[i];//记录x的前导结点
pre[i]=r;//将i的前导结点设置为r根节点
i=j;
}
return r;
}
void UnionSet(int x, int y){
int xp = find(x);
int yp = find(y);
pre[xp] = yp;
}
int main(){
int m,n;
cin>>m>>n;
char a[m][n];
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
cin>>a[i][j];
pre[n*i+j]=n*i+j;
}
}
int flag=0;
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
if(i<m-1&&a[i][j]==a[i+1][j]){
if(find(i*n+j)!=find((i+1)*n+j)){
UnionSet(i*n+j,(i+1)*n+j);
}
else
flag=1;
}
if(j<n-1&&a[i][j]==a[i][j+1]){
if(find(i*n+j)!=find(i*n+j+1)){
UnionSet(i*n+j,i*n+j+1);
}
else
flag=1;
}
if(flag==1) break;
}
if(flag==1) break;
}
if(flag==1)
cout<<"yes";
else
cout<<"no";
return 0;
}