链接: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;
}
天下武功,唯快不破
浙公网安备 33010602011771号