hdoj1232 畅通工程//并查集

本文介绍了一种解决城镇交通连通问题的方法,通过并查集算法确定最少需要新增多少条道路以实现任意两城镇间的交通可达。算法首先将每个城镇初始化为其自身的父节点,然后逐条处理已有的道路信息,合并相关联的城镇集合,最终统计未完全连接的城镇集群数量以确定所需新增道路的数量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

畅通工程

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;
}

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值