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