并查集经典例题分析

http://acm.hdu.edu.cn/showproblem.php?pid=1232
 
1.某省调查城镇交通状况,得到现有城镇道路统计表,表中列出了每条道路直接连通的城镇。省政府“畅通工程”的目标是使全省任何两个城镇间都可以实现交通(但不一定有直接的道路相连,只要互相间接通过道路可达即可)。问最少还需要建设多少条道路?
 

 

Input

测试输入包含若干测试用例。每个测试用例的第1行给出两个正整数,分别是城镇数目N ( < 1000 )和道路数目M;随后的M行对应M条道路,每行给出一对正整数,分别是该条道路直接连通的两个城镇的编号。为简单起见,城镇从1到N编号。
注意:两个城市之间可以有多条道路相通,也就是说
3 3
1 2
1 2
2 1
这种输入也是合法的
当N为0时,输入结束,该用例不被处理。

Sample Input
4 2
1 3
4 3
3 3
1 2
1 3
2 3
5 2
1 2
3 5
999 0
0
 

 

Sample Output
1
0
2
998

题解:

  由题意可知,对任意一个"树杈"上的子节点,都可以认为它们是可达的,从这个角度看来,它们是属于一个集合的元素,这样可以通过并查集的思想来实现,方便且快捷!

把相通的两个地方给连接在一起,这样的话就相当于把两个地方所在集合合并,即合并后的集合中的元素是互通的.   那么最终只需要得出一共有多少个集合,比如最终有32个集合

那么,32-1就是最少需要添加的道路数 !

代码:

  

 1 #include<iostream>
 2 using namespace std;
 3 int par[1001];
 4 int rk[1001]={0};
 5 int findroot(int x)
 6 {
 7     if(par[x]==-1) return x;
 8     else return findroot(par[x]);
 9 }
10 
11 void unite(int x,int y)
12 {
13     int xroot=findroot(x),yroot=findroot(y);
14     if(xroot!=yroot)
15     {
16         if(rk[xroot]<rk[yroot])
17         {
18             par[x]=y;
19         }
20         else if(rk[xroot]>rk[yroot])
21         {
22             par[y]=x;
23         }
24         else
25         {
26             par[y]=x;
27             rk[x]++;
28         }
29     }
30 }
31 void init(int n)
32 {
33     for(int i=1;i<=n;i++)
34     {
35         par[i]=-1;
36         rk[i]=0;
37     }
38     return ;
39 }
40 bool same(int x,int y)
41 {
42     return findroot(x)==findroot(y);
43 }
44 int main()
45 {
46     int n,m,count,x,y;
47     while(1)
48     {
49         cin>>n>>m;
50         if(n==0) return 0;
51         init(n);
52         count=n;
53         for(int i=0;i<m;i++)
54         {
55             cin>>x>>y;
56             if(x==0) return 0;
57             //cin>>y;
58             if(findroot(x)!=findroot(y)) count--;//唯有不同源,即确有集合合并才累积 
59             unite(x,y);
60             //count--;
61         }
62         cout<<count-1<<endl;
63         
64     }
65     return 0;
66 }
67 /*
68 int find(int x)
69 {
70     if(par[x]==-1) return x;
71     else return find(par[x]); 
72 } 
73 
74 void unite(int x,int y)
75 {//简单可行,这里并没有按秩合并,所以同常形成的树比较乱,比较高 
76     x=find(x);
77     par[x]=y;
78 }

 

  题干中的重点是,城镇的互通,只要互通就可以看作在一个集合.    集合之间随意两个节点的连线就使得两个集合中所有城镇互通,这显然又组成了一个更大的集合.

 

posted @ 2020-01-31 16:00  SSXFC  阅读(857)  评论(1编辑  收藏  举报