【算法沉淀】刷题笔记:并查集 带权并查集+实战讲解

 🎉🎉欢迎光临🎉🎉

🏅我是苏泽,一位对技术充满热情的探索者和分享者。🚀🚀

🌟特别推荐给大家我的最新专栏《数据结构与算法:初学者入门指南》📘📘

希望能和大家一起学习!共同进步!

这是苏泽的个人主页可以看到我其他的内容哦👇👇

努力的苏泽icon-default.png?t=N7T8http://suzee.blog.csdn.net

当谈论并查集时,我们可以继续使用上述的动物园比喻来解释它的概念。

我们可以把并查集看作是一个动物园管理系统,帮助你管理动物们的归属关系。

在这个动物园中,每个动物都有一个独特的编号,代表一个独立的元素。一开始,每个动物都是独立的,没有与其他动物建立关系。

  1. 初始化(Init()函数)就像是给每个动物分配一个编号和一个独立的笼子。这样,它们就有了一个起始的归属地。

  2. 查找函数(Find()函数)就像是动物们在寻找自己所属的笼子。当你给一个动物的编号,它会告诉你它所在的笼子。这样,你可以快速找到任何动物所属的笼子。

  3. 合并集合函数(Join()函数)就像是把两个笼子合并在一起,让两个动物的集合变成一个更大的集合。当你把两个动物放在同一个笼子里,它们就成为了同一个集合,共享同一个归属地。

class UnionFind {
    private int[] parent;

    public UnionFind(int size) {
        parent = new int[size];
        for (int i = 0; i < size; i++) {
            parent[i] = i; // 每个动物初始时独立成为一个集合,自己是自己的根节点
        }
    }

    public int find(int x) {
        if (parent[x] != x) {
            parent[x] = find(parent[x]); // 使用路径压缩优化,将当前动物的父节点直接指向根节点
        }
        return parent[x]; // 返回动物所属的笼子(根节点)
    }

    public void join(int x, int y) {
        int rootX = find(x);
        int rootY = find(y);
        if (rootX != rootY) {
            parent[rootX] = rootY; // 将两个笼子合并,让一个根节点指向另一个根节点
        }
    }
}

历届试题 国王的烦恼
问题描述
C 国由 n nn 个小岛组成,为了方便小岛之间联络,C 国在小岛间建立了 m mm 座大桥,每座大桥连接两座小岛。两个小岛间可能存在多座桥连接。然而,由于海水冲刷,有一些大桥面临着不能使用的危险。

如果两个小岛间的所有大桥都不能使用,则这两座小岛就不能直接到达了。然而,只要这两座小岛的居民能通过其他的桥或者其他的小岛互相到达,他们就会安然无事。但是,如果前一天两个小岛之间还有方法可以到达,后一天却不能到达了,居民们就会一起抗议。

现在 C 国的国王已经知道了每座桥能使用的天数,超过这个天数就不能使用了。现在他想知道居民们会有多少天进行抗议。

输入格式
输入的第一行包含两个整数 n , m n, mn,m,分别表示小岛的个数和桥的数量。
接下来 m mm 行,每行三个整数 a , b , t a, b, ta,b,t,分别表示该座桥连接 a aa 号和 b bb 号两个小岛,能使用t天。小岛的编号从 1 开始递增。

输出格式
输出一个整数,表示居民们会抗议的天数。

样例输入
4 4
1 2 2
1 3 2
2 3 1
3 4 3

样例输出
2
样例说明
第一天后 2 和 3 之间的桥不能使用,不影响。
第二天后 1 和 2 之间,以及1和3之间的桥不能使用,居民们会抗议。
第三天后 3 和 4 之间的桥不能使用,居民们会抗议。

数据规模和约定
对于 30% 的数据,1 ≤ n ≤ 20 , 1 ≤ m ≤ 100 1\leq n \leq 20,1 \leq m \leq 1001≤n≤20,1≤m≤100;
对于 50% 的数据,1 ≤ n ≤ 500 , 1 ≤ m ≤ 10000 1 \leq n \leq 500,1 \leq m \leq 100001≤n≤500,1≤m≤10000;
对于 100% 的数据,1 ≤ n ≤ 10000 , 1 ≤ m ≤ 100000 , 1 ≤ a , b ≤ n , 1 ≤ t ≤ 100000 1 \leq n \leq 10000,1 \leq m \leq 100000,1\leq a, b \leq n, 1 \leq t \leq 1000001≤n≤10000,1≤m≤100000,1≤a,b≤n,1≤t≤100000。
 

首先,我们需要根据输入的桥的信息构建并查集。

对于每座桥,如果它的使用天数超过了指定的天数,我们将这两个小岛合并成同一个集合。如果它的使用天数没有超过指定的天数,说明这座桥可以使用,我们不需要对这两个小岛进行合并。

接下来,我们遍历所有的桥,对于每座桥,我们查找连接的两个小岛是否属于同一个集合。如果不属于同一个集合,说明这两个小岛之间没有其他路径可以到达,居民们会抗议的天数加一。

最后,输出居民们会抗议的天数即可。

import java.util.*;

class UnionFind {
    private int[] parent;

    public UnionFind(int size) {
        parent = new int[size + 1];
        for (int i = 1; i <= size; i++) {
            parent[i] = i;
        }
    }

    public int find(int x) {
        if (parent[x] != x) {
            parent[x] = find(parent[x]);
        }
        return parent[x];
    }

    public void union(int x, int y) {
        int rootX = find(x);
        int rootY = find(y);
        if (rootX != rootY) {
            parent[rootX] = rootY;
        }
    }
}

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int m = scanner.nextInt();

        UnionFind uf = new UnionFind(n);

        for (int i = 0; i < m; i++) {
            int a = scanner.nextInt();
            int b = scanner.nextInt();
            int t = scanner.nextInt();
            if (t <= 2) {
                uf.union(a, b);
            }
        }

        int protestDays = 0;
        for (int i = 1; i <= n; i++) {
            if (uf.find(i) == i) {
                protestDays++;
            }
        }

        System.out.println(protestDays - 1);
    }
}

第二道题

问题描述
        小蓝国是一个水上王国, 有 2021 个城邦, 依次编号 1 到 2021。在任意两 个城邦之间, 都有一座桥直接连接。

        为了庆祝小蓝国的传统节日, 小蓝国政府准备将一部分桥装饰起来。

        对于编号为 a 和 b 的两个城邦, 它们之间的桥如果要装饰起来, 需要的费 用如下计算:

        找到 a 和 b 在十进制下所有不同的数位, 将数位上的数字求和。

        例如, 编号为 2021 和 922 两个城邦之间, 千位、百位和个位都不同, 将这些数位上的数字加起来是 (2+0+1)+(0+9+2)=14 。注意 922 没有千位, 千位看成 0 。

        为了节约开支, 小蓝国政府准备只装饰 2020 座桥, 并且要保证从任意一个 城邦到任意另一个城邦之间可以完全只通过装饰的桥到达。

        请问, 小蓝国政府至少要花多少费用才能完成装饰。

        提示: 建议使用计算机编程解决问题。

答案提交
        这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一 个整数, 在提交答案时只填写这个整数, 填写多余的内容将无法得分。

这道题有两个思路:

1.动态规划

思路讲解

首先,我们定义一个二维数组dp,其中dp[i][j]表示城邦i到城邦j之间需要装饰的费用。

然后,我们可以使用动态规划的思路来计算dp数组的值。对于每对城邦(i, j),我们可以通过考虑最后一段路径(i, k, j)来计算dp[i][j]的值,其中k是城邦j的前一个城邦。

具体地,我们可以遍历城邦k的所有可能取值(从1到2021),然后计算dp[i][j]的值。我们可以将dp[i][j]初始化为dp[i][k] + dp[k][j],然后再添加城邦kj之间的装饰费用cost(k, j)。其中cost(k, j)可以通过将城邦kj的编号转换为字符串,然后遍历字符串中的每个字符,将字符转换为数字并求和得到。

最后,我们需要计算小蓝国政府至少要花费的费用,即dp[1][2021]

public class Main {
    public static int calculateCost(int x, int y) {
        String strX = String.valueOf(x);
        String strY = String.valueOf(y);
        int cost = 0;
        for (char digit : strX.toCharArray()) {
            if (strY.contains(String.valueOf(digit))) {
                cost += Character.getNumericValue(digit);
            }
        }
        return cost;
    }

    public static void main(String[] args) {
        int[][] dp = new int[2022][2022];
        for (int i = 1; i <= 2021; i++) {
            for (int j = 1; j <= 2021; j++) {
                if (i != j) {
                    dp[i][j] = calculateCost(i, j);
                }
            }
        }

        for (int k = 1; k <= 2021; k++) {
            for (int i = 1; i <= 2021; i++) {
                for (int j = 1; j <= 2021; j++) {
                    if (i != j && i != k && j != k) {
                        dp[i][j] = Math.min(dp[i][j], dp[i][k] + dp[k][j]);
                    }
                }
            }
        }

        int answer = dp[1][2021];
        System.out.println(answer);
    }
}

2.并查集

题目将城堡看作连通带权无向图,其中城堡的编号表示图的节点,城堡之间的桥梁装饰费用表示图的边权。

首先,我们定义一个并查集数据结构,用于合并城堡所属的连通分量。

然后,我们遍历所有的桥梁,计算每座桥梁的装饰费用,并将费用作为边权存储在一个二维数组dp中。

接下来,我们使用并查集的思想,将连接费用为0的城堡合并到同一个连通分量中。

最后,我们计算所有城堡到第一个城堡的装饰费用,即累加每个连通分量中的最小边权。

这样,我们就可以得到小蓝国政府至少要花费的费用。

import java.util.Arrays;

public class Main {
    public static class UnionFind {
        private int[] parent;
        private int[] rank;

        public UnionFind(int n) {
            parent = new int[n];
            rank = new int[n];
            Arrays.fill(rank, 1);
            for (int i = 0; i < n; i++) {
                parent[i] = i;
            }
        }

        public int find(int x) {
            if (parent[x] != x) {
                parent[x] = find(parent[x]);
            }
            return parent[x];
        }

        public void union(int x, int y) {
            int rootX = find(x);
            int rootY = find(y);
            if (rootX != rootY) {
                if (rank[rootX] > rank[rootY]) {
                    parent[rootY] = rootX;
                } else if (rank[rootX] < rank[rootY]) {
                    parent[rootX] = rootY;
                } else {
                    parent[rootY] = rootX;
                    rank[rootX]++;
                }
            }
        }
    }

    public static int calculateCost(int x, int y) {
        String strX = String.valueOf(x);
        String strY = String.valueOf(y);
        int cost = 0;
        for (char digit : strX.toCharArray()) {
            if (strY.contains(String.valueOf(digit))) {
                cost += Character.getNumericValue(digit);
            }
        }
        return cost;
    }

    public static void main(String[] args) {
        int n = 2021;
        UnionFind uf = new UnionFind(n + 1);
        int[][] dp = new int[n + 1][n + 1];

        // 构建并查集
        for (int i = 1; i <= n; i++) {
            for (int j = i + 1; j <= n; j++) {
                int cost = calculateCost(i, j);
                dp[i][j] = cost;
                dp[j][i] = cost;
                if (cost == 0) {
                    uf.union(i, j);
                }
            }
        }

        // 合并连通分量
        int[] set = new int[n + 1];
        Arrays.fill(set, -1);
        for (int i = 1; i <= n; i++) {
            int root = uf.find(i);
            if (set[root] == -1) {
                set[root] = i;
            }
        }

        // 计算最小装饰费用
        int answer = 0;
        for (int i = 1; i <= n; i++) {
            if (set[i] != -1) {
                answer += dp[1][set[i]];
            }
        }

        System.out.println(answer);
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/426693.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Windows Server 各版本搭建文件服务器实现共享文件(03~19)

一、Windows Server 2003 打开服务器&#xff0c;点击左下角开始➡管理工具➡管理您的服务器➡添加或删除角色 点击下一步等待测试 勾选自定义配置&#xff0c;点击下一步 选择文件服务器&#xff0c;点击下一步 勾选设置默认磁盘空间&#xff0c;数据自己更改&#xff0c;最…

Onenote软件新建笔记本时报错:无法在以下位置新建笔记本

报错现象&#xff1a; 当在OneNote软件上&#xff0c;新建笔记本时&#xff1a; 然后&#xff0c;尝试重新登录微软账户&#xff0c;也不行&#xff0c;提示报错&#xff1a; 解决办法&#xff1a; 打开一个新的记事本&#xff0c;复制粘贴以下内容&#xff1a; C:\Users\Adm…

如何防御跨站请求伪造(CSRF)攻击?

CSRF 英文全称是 Cross-site request forgery&#xff0c;所以又称为“跨站请求伪造”&#xff0c;是指恶意诱导用户打开被精心构造的网站&#xff0c;在该网站中&#xff0c;利用用户的登录状态发起的跨站请求。简单来讲&#xff0c;CSRF 就是利用了用户的登录状态&#xff0c…

WordPress建站入门教程:如何在本地电脑搭建WordPress网站?

前面跟大家分享了『WordPress建站入门教程&#xff1a;如何安装本地WordPress网站运行环境&#xff1f;』&#xff0c;接下来boke112百科就继续跟大家分享本地电脑如何搭建WordPress网站。 小皮面板&#xff08;phpstudy&#xff09;的“软件管理 – 网站程序”虽然可以一键部…

excel统计分析——拉丁方设计

参考资料&#xff1a;生物统计学 拉丁方设计也是随机区组设计&#xff0c;是对随机区组设计的一种改进。它在行的方向和列的方向都可以看成区组&#xff0c;因此能实现双向误差的控制。在一般的试验设计中&#xff0c;拉丁方常被看作双区组设计&#xff0c;用于提高发现处理效应…

身份证识别系统(安卓)

设计内容与要求&#xff1a; 通过手机摄像头捕获身份证信息&#xff0c;将身份证上的姓名、性别、出生年月、身份证号码保存在数据库中。1&#xff09;所开发Apps软件至少需由3-5个以上功能性界面组成。要求&#xff1a;界面美观整洁、方便应用&#xff1b;可以使用Android原生…

徽标键锁定问题

徽标键锁定问题 1. 锁定徽标键2. 解锁徽标键 无意中发现键盘除了左右徽标键&#xff0c;其余键都正常。相关的组合键也都失效。 自己的键盘是ikbc w210款的键盘。一直使用都没有任何问题。 搜索发现使用 Fn和 徽标键组合就能锁定和解锁 徽标键。 1. 锁定徽标键 左徽标键Fn …

[项目设计] 从零实现的高并发内存池(一)

&#x1f308; 博客个人主页&#xff1a;Chris在Coding &#x1f3a5; 本文所属专栏&#xff1a;[高并发内存池] ❤️ 前置学习专栏&#xff1a;[Linux学习] ⏰ 我们仍在旅途 ​ 目录 前言 项目介绍 1.内存池 1.1 什么是内存池 池化技术 内存池 1.2 为什…

思科网络设备监控

思科是 IT 行业的先驱之一&#xff0c;提供从交换机到刀片服务器的各种设备&#xff0c;以满足中小企业和企业的各种 IT 管理需求。管理充满思科的 IT 车间涉及许多管理挑战&#xff0c;例如监控可用性和性能、管理配置更改、存档防火墙日志、排除带宽问题等等&#xff0c;这需…

区块链媒体发布推广10个热门案例解析-华媒舍

区块链技术的发展已经引起了媒体的广泛关注&#xff0c;越来越多的区块链媒体纷纷发布推广相关的热门案例。本文将介绍10个成功的区块链媒体推广案例&#xff0c;并分享它们的成功秘诀&#xff0c;帮助读者更好地了解区块链媒体推广的方法与技巧。 随着区块链技术的成熟和应用场…

常用的电阻、电容的种类和应用场合?

电阻的 a.按阻值特性:固定电阻、可调电阻、特种电阻(敏感电阻)&#xff0c;不能调节的,我们称之为固定电阻,而可以调节的,我们称之为可调电阻.常见的例如收音机音量调节的,主要应用于电压分配的,我们称之为电位器. b.按制造材料:碳膜电阻、金属膜电阻、线绕电阻&#xff0c;捷…

Qt/C++音视频开发67-保存裸流加入sps/pps信息/支持264/265裸流/转码保存/拉流推流

一、前言 音视频组件除了支持保存MP4文件外&#xff0c;同时还支持保存裸流即264/265文件&#xff0c;以及解码后最原始的yuv文件。在实际使用过程中&#xff0c;会发现部分视频文件保存的裸流文件&#xff0c;并不能直接用播放器播放&#xff0c;查阅资料得知原来是缺少sps/p…

2023年,我的年终总结

序言 2023年的年终总结一直拖到现在&#xff0c;想来是有多个原因吧&#xff1a;第一个应该是年底还有些事情没有完成&#xff0c;内心有所不甘&#xff1b;第二个应该是这一年似乎是很忙碌的一年&#xff0c;不知从何说起&#xff1b;第三个应该是对于自己这一年的收获&#…

Learning from Unlabeled 3D Environments forVision-and-Language Navigation

这篇论文是关于高级指令的 摘要 在视觉和语言导航 (VLN) 中&#xff0c;实体代理需要按照自然语言指令在真实的 3D 环境中进行导航。现有 VLN 方法的一个主要瓶颈是缺乏足够的训练数据&#xff0c;导致对未见过的环境的泛化效果不理想。虽然 VLN 数据通常是手动收集的&#x…

动态SQL的处理

学习视频&#xff1a;3001 动态SQL中的元素_哔哩哔哩_bilibili 目录 1.1为什么学 1.2动态SQL中的元素 条件查询操作 if 元素 choose、when、otherwise元素 where、trim元素 更新操作 set元素使用场景 复杂查询操作 foreach 元素中的属性 ​编辑 迭代数组 迭代List 迭代Map 1…

RocketMQ - 深入研究一下Broker是如何持久化存储消息的

1. CommitLog消息顺序写入机制 首先思考一下,当生产者的消息发送到一个Broker上的时候,他接收到了一条消息,接着他会对这个消息做什么事情? 首先第一步,他会把整个消息直接写入磁盘上的一个日志文件,叫做CommitLog,直接顺序写入这个文件,如下图: 这个CommitLog是很…

Python用类实现抽象和封装

归纳编程学习的感悟&#xff0c; 记录奋斗路上的点滴&#xff0c; 希望能帮到一样刻苦的你&#xff01; 如有不足欢迎指正&#xff01; 共同学习交流&#xff01; &#x1f30e;欢迎各位→点赞 &#x1f44d; 收藏⭐ 留言​&#x1f4dd; 路在脚下&#xff0c;勇往直前&#x…

mac报错:zsh: command not found: npm

1、问题概述&#xff1f; 在mac系统中使用npm命令的时候&#xff0c;mac os报错提示&#xff1a; zsh: command not found: npm 一般出现发这种情况的原因时没有安装npm,而npm这命令时集成在nodejs中的&#xff0c;所以安装nodejs就可以了。 2、解决办法 本质就是需要安装…

Day13:信息打点-JS架构框架识别泄漏提取API接口枚举FUZZ爬虫插件项目

目录 JS前端架构-识别&分析 JS前端架构-开发框架分析 前端架构-半自动Burp分析 前端架构-自动化项目分析 思维导图 章节知识点 Web&#xff1a;语言/CMS/中间件/数据库/系统/WAF等 系统&#xff1a;操作系统/端口服务/网络环境/防火墙等 应用&#xff1a;APP对象/API接…

聊一聊Python量化交易

在金融领域&#xff0c;量化交易已经成为一种越来越受欢迎的交易方式。它通过使用数学模型来分析市场&#xff0c;自动化执行交易决策&#xff0c;以此来获取超额回报。近年来&#xff0c;Python因其简洁易学、功能强大而成为量化交易领域的首选编程语言。本文将详细介绍Python…