链接: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; }