通俗易懂——并查集详解!!!——HDU 1232畅通工程

本文介绍了一种利用并查集数据结构解决道路连通性问题的方法,通过具体实例详细阐述了如何使用并查集来计算使所有城市相互连通所需的最少新增道路数量。

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

第一次写博客还有点小小的激动!!!
并查集
首先我们需要明确,并查集是用来干什么的,什么时候需要用并查集
给出一道经典例题
HDU1232畅通工程
题意

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

输入

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

输出

对每个测试用例,在1行里输出最少还需要建设的道路数目。

样例输入

4 2
1 3
4 3
3 3
1 2
1 3
2 3
5 2
1 2
3 5
999 0
0

样例输出

1
0
2
998

大致题意是给出N给城市 其中两两城市间共有有M条路,如果要让这几个城市连通(从任意一个城市可以到其他所有城市)需要再修几条路

解析
这道题的关键就在于我们如何表示两个城市是有通路的
所以我们在这里引进了并查集的概念
我们建立一个数组 root[n]
假设每一个城市都有一个领导人,但是相连通的城市的领导人为同一人
那么最开始时,每一个城市都有自己独一无二的领导人
所以我们初始化

for(int i=1;i<=n;i++)
    {
        root[i]=i;     // 表示i城的领导人为i
    }

当输入题目中的路程后
以第一个样例来解析
输入 4 2 表示
4个城市2 条路
1 3 表示1号城市与3号城市之间有路,所以是连通的
所以我们接下来进行操作
查看1号城市的领导人和3号城市是不是同一个
所以这里就有一个查找函数

int fi(int n)
{
    int x=n;
    while(root[x]!=x)
    {
        x=root[x];//查找函数不明白可以先往后看,再回来理解
    }
     return x;
}

比如现在我查找到fi(1)是不等于fi(3)的,也就是说,1,3原来的领导人不是同一个,那么1,3原来就是不连通的,所以我们就要修路将他们连通
如何连通呢?
很简单,只需要把3号城的领导人改成1号城的领导人,这样1,3城的领导人就是同一个,他们就连通了
那么进行操作

void unite(int a,int b)
{
    int x=fi(a); int y=fi(b);//先找到两个城市的领导人
    if(x!=y)         //如果领导人不为同一个的话
    {
        root[x]=y;//x的领导人改成y
    }
}

这里解释一下上面查找的问题
因为我们每次修改领导人 都是把一个城市的领导人改成另外一个城市的领导人
但是每次操作,总有一个城市的领导人是原本的领导人 即root[i]=i;
我们查找时,查找到root[i] != i 说明 i 城的领导人是 root[i] 城的领导人 ,所以我们要对root[i]进行查找 直到找到一个root[j]=j的城市 这给城市的领导人就是i城的领导人

完整代码

#include<bits/stdc++.h>
using namespace std;
int n,m;
int ans=0;
int root[1010];  //保持每个城市的领导人
int fi(int n)
{
    int x=n;
    while(root[x]!=x)
    {
        x=root[x];  //查找领导人
    }
          return x; //返回领导人

}
void unite(int x,int y)
{
    if(x!=y)
    {
        root[x]=y;  //将两座城市合并 ,领导人变成一个
    }
    ans++;  //每次操作计数

}
int main()
{
    while(~scanf("%d",&n)&&n!=0)
    {
        scanf("%d",&m);
        ans=0;
        for(int i=1;i<=n;i++)
    {
        root[i]=i;  //初始化
    }
    for(int i=0;i<m;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        int a=fi(x); int b=fi(y);//查找两个城市的领导人
        if(a!=b)
        {
            unite(a,b); //如果领导人相同就无需操作,否则需要将他们相连
        }
    }

     printf("%d\n",n-ans-1);//总城市数减去连通次数再减一就为还需修路的条数
    }

    return 0;
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值