HDU 1232 畅通工程(并查集分析)

链接:http://acm.hdu.edu.cn/showproblem.php?pid=1232

并查集,大一没学数据结构,所以把这道题放了一段时间,当时百度了好多并查集的资料,还是没弄明白,今天看到有人传了个数据结构的PPT.。就下载下来看了下并查集的部分。正好在这里写下分析:

根据我的理解,并查集就是维护了几个动态的集合,集合中的每一个元素都标记了一个父节点,同一个集合的代表是相同的。当一个元素的父节点就是他本身时,它就是该集合的代表。

并查集有三种操作:

1>make(x).用于初始化集合,将每个元素的父节点设置为他本身。即表示当前一个元素为一个集合,互相没有联系

2>union(x,y)合并x,和y所在的集合。即把y的代表设置为x所在集合的代表。

3>find(x),;返回x所在集合的代表

这里用数组实现,int father[i]....其中i表示元素,father[i]的值指向它的父节点。

根据代表的父节点就是他本身这个特点,找出一个元素的代表:

int find(int x)                  //用非递归的实现
  {
      while (father[x] != x) x = father[x];
      return x;
  }
int find(int x)                   //用递归的实现
  {
      if (father[x] != x) return find(father[x]);
      else return x;
  } 


合并,即把一个元素所在集合的代表的父节点 设置为 另一个元素所在集合的代表:

void combine(int a,int b)   //合并
{
	int ta=find(a);
	int tb=find(b);

	if(ta!=tb)
		father[ta]=tb;
}


 

所以,根据并查集判断两个元素是否有联系,只需判断他们是否在同一个集合里即可,也就是判断他们的代表是不是同一个

没有经过任何优化.................

AC代码:

#include<iostream>
using namespace std;

const int MAX=1000;
int father[MAX];

void make(int n)    //初始化
{
	for(int i=1;i<=n;i++)
		father[i]=i;
}

int find(int x)    //查找
{
	while(father[x]!=x)
		x=father[x];

	return x;
}

void combine(int a,int b)   //合并
{
	int ta=find(a);
	int tb=find(b);

	if(ta!=tb)
		father[ta]=tb;
}

int main()
{
	int i,n,m,a,b,tmp;

	while(cin>>n,n)
	{
		make(n);
		cin>>m;
		for(i=1;i<=m;i++)
		{
			cin>>a>>b;
			combine(a,b);
		}

		tmp=0;
		for(i=1;i<=n;i++)   //确定连通分量个数
			if(father[i]==i)
				tmp++;
		
		cout<<tmp-1<<endl;
	}

	return 0;
}


 

原文地址:https://www.cnblogs.com/frankM/p/4399556.html