畅通工程
Time Limit: 4000/2000 MS(Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 21508 Accepted Submission(s): 11157
Problem Description
某省调查城镇交通状况,得到现有城镇道路统计表,表中列出了每条道路直接连通的城镇。省政府“畅通工程”的目标是使全省任何两个城镇间都可以实现交通(但不一定有直接的道路相连,只要互相间接通过道路可达即可)。问最少还需要建设多少条道路?
Input
测试输入包含若干测试用例。每个测试用例的第1行给出两个正整数,分别是城镇数目N ( < 1000 )和道路数目M;随后的M行对应M条道路,每行给出一对正整数,分别是该条道路直接连通的两个城镇的编号。为简单起见,城镇从1到N编号。
注意:两个城市之间可以有多条道路相通,也就是说
3 3
1 2
1 2
2 1
这种输入也是合法的
当N为0时,输入结束,该用例不被处理。
Output
对每个测试用例,在1行里输出最少还需要建设的道路数目。
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
Hint
Hint
Huge input, scanf is recommended.
Source
题目大意:
题目告诉我们,一些城镇为方便合作交流,现需修建道路,在所给出的若干个城镇中,已知已有一些城镇之间已经建立了联系,政府所要做的是保证每两个城镇之间都有路径可达,当然并不一定是直接可达,那么请你给出所要修建的道路的最少条数。
题目解析:
该题属于赤裸裸的并查集,也许你会问,什么是并查集,并查集能够做什么?下面我先来介绍并查集的概念,所谓并查集,就是“并”和“查”,而这两个操作则建立在集合的基础之上。百度文库上说,在一些有N个元素的集合应用问题中,我们通常是在开始时让每个元素构成一个单元素的集合,然后按一定顺序将属于同一组的元素所在的集合合并,其间要反复查找一个元素在哪个集合中。这样的说法很通俗,但是并不能让我们很好的理解并查集。在这里,我们结合本例来说明,本题中出现的城镇,我们可以抽象成点,那么城镇与城镇之间的关系我们可以抽象成点与点之间的联系,我们用“线”来表示。如果我们将所有单独的点看成一个集合,而将所有有联系的集合合并为一个统一的集合,这样不同的集合就可以表示未能取得联系的城镇群的个数。即有N个城镇,假设彼此都并不可达,那么要想建立联系,则至少需要修N-1条路。每一个集合中,我们找一个最小的元素做代表,抽象到树中,我们理解查找与合并操作分别为:
将两个元素所在的集合合并为一个集合,即将一棵树的根节点指向另一棵树的根节点。
具体主要算法实现如下:
void Init()
{
for(inti=0;i<n;i++)
father[i]=i;
}
\int Find(int x) //查找当前节点的祖先(根)
{
if(father[x]!=x)
father[x]=Find(father[x]);
returnfather[x];
}
void merge(int a,int b)
{
intp=Find(a);
intq=Find(b);
if(p!=q)
{
father[q]=p;
}
}
#include <iostream>
#include <stdio.h>
using namespace std;
int Set[1005],sum;
int Find(int x)
{
if(Set[x]!=x)
Set[x]=x;
return Set[x];
}
void Merge(int a,int b)
{
int p=Find(a);
int q=Find(b);
if(p!=q)
{
Set[q]=p;
sum++;
}
}
int main()
{
int n,m,i,x,y;
while(cin>>n>>m&&n)
{
for(i=1;i<=n;++i)
Set[i]=i;
sum=0;
for(i=1;i<=m;++i)
{
cin>>x>>y;
Merge(x,y);
}
cout<<n-1-sum<<endl;
}
return 0;
}