牛客网练习赛8 加边无向图(并查集)

题目地址
java和c++都有
这里写图片描述

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 32768K,其他语言65536K
64bit IO Format: %lld
题目描述

给你一个 n 个点,m 条边的无向图,求至少要在这个的基础上加多少条无向边使得任意两个点可达~
输入描述:

第一行两个正整数 n 和 m 。
接下来的m行中,每行两个正整数 i 、 j ,表示点i与点j之间有一条无向道路。

输出描述:

输出一个整数,表示答案

示例1
输入

4 2
1 2
3 4

输出

1

备注:

对于100%的数据,有n,m<=100000。


/*
整体思路,用数组做并查集问题
根据题目要求创建数组pre,完成对数组的初始化(这道题下标从1开始)
ps:若pre[3]=7;那么视为3的上级是7 下标不可重复,值可重复,对应一个父节点可以对应多个子节点 
默认无边,每个节点都是自己的主元
默认要加n-1条边(题目保证输入是正整数,不需要考虑0的情况)用total计数
然后每录入一条边的数组,将边加入图中,如果属于同一个连通分支total不变(加了一个环)
如果是不同分支,那么total--
total就是需要加的边
*/

import java.util.Scanner;

public class Main {//并查集
    static int pre[]=new int[100001];
    public static void main(String[] args) {
        Scanner input=new Scanner(System.in);
        int n=input.nextInt();
        int m=input.nextInt();
        init(pre, n);
        int total=n-1;
        while (m>0) {
            int x=input.nextInt();
            int y=input.nextInt();
            int f1=find(x);
            int f2=find(y);
            if (f1!=f2) {
                pre[f1]=f2;
                total--;
            }

            m--;
        }
        System.out.println(total);
    }
    //初始化
    public static void init(int pre[],int n){
        for (int i = 1; i <= n; i++) {
            pre[i]=i;
        }
    }
    //寻找主元
    public static int find(int x) {
        int r=x;
        while (pre[r]!=r) {
            r=pre[r];
        }
        return r;
    }
    //加边
    public static void join(int x,int y){
        x=find(x);
        y=find(y);
        if(x!=y){
            pre[x]=y;//x入赘到y上 
        }

    }
    //补充的内容
    /*
     * 在这道题,因为没有层次关系,可以在查找的时候用路径压缩来优化算法,
     * 减少查找主元的次数,具体过程就是让子节点找到后直接与主元相连
     * 
     * 非递归写法(压缩路径)
     */
    public static int find2(int x) {
        int r=x;
        while(pre[r]!=r){//自己不是代表元 
            r=pre[r]; //让自己的上级再去找上级 
        }
        int i=x;int j; 
        while(i!=r){
            j=pre[i];
            pre[i]=r;
            i=j;
        }
        return r;
    }
    //数组用递归的方式寻找主元
    public static int find3(int x){//数组 递归方法找主元
        return pre[x]!=x?pre[x]=find3(pre[x]) : x;
    }
    //数组递归写法2
    //递归写法
    public static int find4(int x)
    {
        if(pre[x] != x)
        {
            pre[x] = find(pre[x]);  //找的祖先递归赋值,将本次查找经过的每个节点都直接连到祖先节点上
        }
        return pre[x];
    }
}
#include<iostream>
#include<cstdio>
int pre[100000];//根据题目的数据规模 
using namespace std;
//并查集 --加边无向图 
//若pre[3]=7;那么视为3的上级是7 下标不可重复,值可重复,对应一个父节点可以对应多个子节点 
int find(int x) {
    int r=x;
    while(pre[r]!=r){//自己不是代表元 
        r=pre[r]; //让自己的上级再去找上级 
    }
/* int i=x;int j; 
   while(i!=r){
        j=pre[i];
        pre[i]=r;
        i=j;
    }*/
    return r;
}
void join(int x,int y){
    int fx=find(x),fy=find(y);
    if(fx!=fy){
        pre[fx]=fy;//x入赘到y上 
    }
}
void init(int pre[],int n){//自己是自己的代表元 
    for(int i=1;i<=n;i++){
        pre[i]=i;
    }
}
int main(){
    int n,m ,p1,p2,i,total,f1,f2;
    scanf("%d %d",&n,&m);//正整数已经确保n!=0 
    init(pre,n);
    total=n-1;
    //join函数修改以适应题目 
    while(m--){
        scanf("%d %d",&p1,&p2);
        f1=find(p1);
        f2=find(p2);
        if(f1!=f2){
            pre[f1]=f2;
            total--;
        }
    }
    printf("%d\n",total);

    return 0;
} 
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值