畅通工程
Time Limit : 4000/2000ms (Java/Other) Memory Limit : 65536/32768K (Java/Other)
Total Submission(s) : 24 Accepted Submission(s) : 14
Font: Times New Roman | Verdana | Georgia
Font Size: ← →
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
题意:
要使整个图连通,至少还需几条边? 点--城镇 边--道路
思路:
每个节点都归属于某个集合(树或子树),该集合以其中一个节点(起始顶点Root)为标识。
一个节点r的集合标识(顶点Root) 通过它的上一级节点father[r]来查找,上级节点再往上查找自己的上级节点,依此类推,直到找到一个这样的节点:father[r]=r,没有上级节点或上级节点是自身的节点(顶点Root,集合标识节点)。
解题的步骤:
1) 程序开始时进行归并(并查)初始化: father[i]=i, 把每个节点的顶点都初始化成自己
2) 依次读取每一条边<x,y>,分别查找x和y的归属集合(过程如上面所述)标识rx和ry,
如果rx和ry不同,说明两者归属不同的集合(子树,连通),则让ry成为rx的上级节点,
即father[rx]=ry, 这样rx所在子树(子连通区)就归并到ry集合(子树,连通)且以ry
为起始顶点Root(集合标识)。
3) 当所有边按步骤2的描述过程处理后,归并任务就完成了。最后只要统计连通的
片数(有几个顶点即上级节点是自己)num。题解就是num-1。
#include<iostream>
int father[1005];//记录每个节点的上级节点,通过它查找节点所归并的集合(连通)名称--root
//找出顶点x的归属集合---以归并树的root来命名---集合(连通)名称
int find(int x)
{
if(x!=father[x])
father[x]=find(father[x]);
return father[x];
}
//find函数也可以这么写
/*
int find(int x)
{
int r=x;
while( father[r] !=r )
r = father[r];
return r;
}
*/
//把x归并到y,即让x的集合标识(father[x])用y的赋
void Union(int x,int y)
{
int rx=find(x);//归属集合标识即子树的root
int ry=find(y);
if(rx!=ry)
father[rx]=ry;
}
int main()
{
using namespace std;
int N,M,x,y;
while(cin>>N&&N)
{
for(int i=1;i<=N;i++)
father[i]=i;
cin>>M;
while(M--)
{
cin>>x>>y;
Union(x,y);
}
int num=-1;
for(int i=1;i<=N;++i)
if(father[i]==i)
num++;
cout<<num<<endl;
}
return 0;
}