并查集入门题集

畅通工程

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

INPUT

  • 测试输入包含若干测试用例。每个测试用例的第1行给出两个正整数,分别是城镇数目N ( < 1000 )和道路数目M;随后的M行对应M条道路,每行给出一对正整数,分别是该条道路直接连通的两个城镇的编号。为简单起见,城镇从1到N编号。 

  • 注意:两个城市之间可以有多条道路相通,也就是说
    3 3
    1 2
    1 2
    2 1

  • 这种输入也是合法的

  • 当N为0时,输入结束,该用例不被处理。 

OUTPUT

  • 对每个测试用例,在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个城市,这n个城市由m条路相连。
  • 给出每条路连接的两个城市。
  • 求还需要几条路才可以将全部的城市连接起来。

解题思路:

这道题是并查集的模板题,有人说这是一道生成树,不过我觉得并查集理解起来好理解一些。

就是将已经联通的城市归为一个集合,取出一个城市作为根节点,输入读完之后就已经将联通的城市归为一类了。只要查出有几类就可以了,换句话说只要查出有几个根就可以了,根的特点是他的前驱是它本身。

代码:

#include<iostream>
#include<stdio.h>
using namespace std;
int cnt = 0;
int pre[1005];

int findpre(int x)        //寻找前驱
{
    int r = x;
    while(pre[r]!=r)
        r = pre[r];
    return r;
}
int join(int x,int y)     //合并节点
{
    int fx = findpre(x);  
    int fy = findpre(y);  
    if(fx!=fy)
    {
        pre[fx] = fy;
    }
    return 0;
}

int pl(int x)             //压缩路径,实际上这道题数据量比较小不需要路径压缩
{
    int r = x;
    while(pre[r]!=r)
        r = pre[r];
    int k = x;
    int j;
    while(k!=r)
    {
        j = pre[k];
        pre[k] = r;
        k = j;
    }
    return 0;
}

int main()
{
    while(1)
    {
        int n,m;
        scanf("%d",&n);
        if(n == 0)
            break;
        scanf("%d",&m);
        for(int i=0;i<=n;i++)
        {
            pre[i] = i;
        }
        for(int i=0;i<m;i++)
        {
            int f,t;
            scanf("%d%d",&f,&t);
            join(f,t);
        }
        for(int i=0;i<=n;i++)
        {
            pl(i);
        }
        for(int i = 1;i<=n;i++)
        {
            if(pre[i]==i)
            {
                cnt++;
            }
        }
        cout<<cnt-1<<endl;
        cnt = 0;

    }
    return 0;
}

How Many Tables

Today is Ignatius' birthday. He invites a lot of friends. Now it's dinner time. Ignatius wants to know how many tables he needs at least. You have to notice that not all the friends know each other, and all the friends do not want to stay with strangers.

One important rule for this problem is that if I tell you A knows B, and B knows C, that means A, B, C know each other, so they can stay in one table.

For example: If I tell you A knows B, B knows C, and D knows E, so A, B, C can stay in one table, and D, E have to stay in the other one. So Ignatius needs 2 tables at least.

INPUT

  • The input starts with an integer T(1<=T<=25) which indicate the number of test cases. Then T test cases follow.
  • Each test case starts with two integers N and M(1<=N,M<=1000). N indicates the number of friends, the friends are marked from 1 to N. Then M lines follow. Each line consists of two integers A and B(A!=B), that means friend A and friend B know each other. There will be a blank line between two cases.

OUTPUT

  • For each test case, just output how many tables Ignatius needs at least. Do NOT print any blanks.

测试样例

2
5 3
1 2
2 3
4 5

5 1
2 5

样例输出

2
4

题意理解:

  • 和畅通工程那道题基本一样,只不过这次说的是几个人在一起dinner,认识的人可以坐到一张桌子上,输入有几个人,谁和谁互相认识,需要求要几张桌子。

解题思路:

也是一道并查集模板题。

在每个认识的人里选出一个代表作为整个集合的根节点,最后查找几个根就有几张桌子。

不多说直接粘代码了。

代码:

#include<iostream>

using namespace std;
int pre[1010];
int findpre(int x)
{
    int r = x;
    while(r!=pre[r])
        r = pre[r];
    return r;
}
int join(int x,int y)
{
    int fx = findpre(x);
    int fy = findpre(y);
    if(fx!=fy)
    {
        pre[fx] = fy;
    }
    return 0;
}
int main()
{
    int t;
    cin>>t;
    for(int i=0;i<t;i++)
    {
        int m,n;
        cin>>m>>n;
        for(int j=1;j<=m;j++)
        {
            pre[j] = j;
        }
        for(int j=0;j<n;j++)
        {
            int f,t;
            cin>>f>>t;
            if(f>t)
                join(f,t);
            else
                join(t,f);
        }
        int cnt = 0;
        for(int j=1;j<=m;j++)
        {
            if(pre[j] == j)
            {
                //cout<<pre[j]<<endl;
                cnt++;
            }

        }
        cout<<cnt<<endl;
    }
    return 0;
}


PID331 / 家族

若某个家族人员过于庞大,要判断两个是否是亲戚,确实还很不容易,现在给出某个亲戚关系图,求任意给出的两个人是否具有亲戚关系。

规定:x和y是亲戚,y和z是亲戚,那么x和z也是亲戚。如果x,y是亲戚,那么x的亲戚都是y的亲戚,y的亲戚也都是x的亲戚。

INPUT

  • 第一行:三个整数n,m,p,(n<=5000,m<=5000,p<=5000),分别表示有n个人,m个亲戚关系,询问p对亲戚关系。

  • 以下m行:每行两个数Mi,Mj,1<=Mi,Mj<=N,表示Ai和Bi具有亲戚关系。

  • 接下来p行:每行两个数Pi,Pj,询问Pi和Pj是否具有亲戚关系。

OUTPUT

  • P行,每行一个’Yes’或’No’。表示第i个询问的答案为“具有”或“不具有”亲戚关系。

测试样例

6 5 3
1 2
1 5
3 4
5 2
1 3
1 4
2 3
5 6

样例输出

Yes
Yes
No

题意理解:

  • 给定几个人和几组关系,询问几组数据看是否有关系。

解题思路:

这道题还是一道板子题,直接用并查集就可以求解。解题思路和上面两个题基本一致。

代码:

#include<iostream>

using namespace std;
int pre[5005];
int findpre(int x)
{
    int r = x;
    while(pre[r]!=r)
        r = pre[r];
    return r;
}

int join(int x,int y)
{
    int fx = findpre(x);
    int fy = findpre(y);
    if(fx!=fy)
    {
        pre[fy] = fx;
    }
}

int main()
{
    int n,m,p;
    cin>>n>>m>>p;
    for(int i=1;i<=n;i++)
    {
        pre[i] = i;
    }
    for(int i=0;i<m;i++)
    {
        int x,y;
        cin>>x>>y;
        if(x<y)
            join(x,y);
        else
            join(y,x);
    }
    for(int i=0;i<p;i++)
    {
        int x,y;
        cin>>x>>y;
        if(findpre(x)==findpre(y))
        {
            cout<<"Yes"<<endl;
        }
        else
            cout<<"No"<<endl;
    }

}

 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
查集(Disjoint Set)是一种数据结构,用于解决集合的合并和查找问。在Python中可以使用类来实现并查集。引用展示了一个简单的并查集类的代码实现,其中包括了初始化集合、查找集合、合并集合和判断两个元素是否在同一个集合中的方法。另外,引用和展示了对并查集代码的优化,包括路径压缩和按秩合并等技巧,以提高并查集的效率。 在Python中使用并查集可以解决一些实际问,如求解岛屿个数、朋友圈等。通过将问转化为集合的合并和查找操作,可以使用并查集来高效地解决这些问。 所以,如果你需要在Python中实现并查集,可以参考以上的代码实现和优化方法。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [python 数据结构与算法——并查集](https://blog.csdn.net/itnerd/article/details/103916115)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [并查集Python版](https://blog.csdn.net/XZ2585458279/article/details/127274576)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值