并查集(判断环路)

并查集是非常常用的一种数据结构,用于把数据按照规则整理成集合,集合最终呈现为树状结构,以根节点作为不同集合的区分标志,实现方面主要涉及查找和合并,代码如下

//查找
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;
}

猜你喜欢

转载自blog.csdn.net/qq_35513792/article/details/82620180