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

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

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


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


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


  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],然后再添加城邦k和j之间的装饰费用cost(k, j)。其中cost(k, j)可以通过将城邦k和j的编号转换为字符串,然后遍历字符串中的每个字符,将字符转换为数字并求和得到。


最后,我们需要计算小蓝国政府至少要花费的费用,即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);
    }
}
相关文章
|
2月前
|
算法 数据可视化 测试技术
HNSW算法实战:用分层图索引替换k-NN暴力搜索
HNSW是一种高效向量检索算法,通过分层图结构实现近似最近邻的对数时间搜索,显著降低查询延迟。相比暴力搜索,它在保持高召回率的同时,将性能提升数十倍,广泛应用于大规模RAG系统。
200 10
HNSW算法实战:用分层图索引替换k-NN暴力搜索
|
7月前
|
负载均衡 算法 关系型数据库
大数据大厂之MySQL数据库课程设计:揭秘MySQL集群架构负载均衡核心算法:从理论到Java代码实战,让你的数据库性能飙升!
本文聚焦 MySQL 集群架构中的负载均衡算法,阐述其重要性。详细介绍轮询、加权轮询、最少连接、加权最少连接、随机、源地址哈希等常用算法,分析各自优缺点及适用场景。并提供 Java 语言代码实现示例,助力直观理解。文章结构清晰,语言通俗易懂,对理解和应用负载均衡算法具有实用价值和参考价值。
大数据大厂之MySQL数据库课程设计:揭秘MySQL集群架构负载均衡核心算法:从理论到Java代码实战,让你的数据库性能飙升!
|
2月前
|
机器学习/深度学习 缓存 算法
微店关键词搜索接口核心突破:动态权重算法与语义引擎的实战落地
本文详解微店搜索接口从基础匹配到智能推荐的技术进阶路径,涵盖动态权重、语义理解与行为闭环三大创新,助力商家提升搜索转化率、商品曝光与用户留存,实现技术驱动的业绩增长。
|
4月前
|
机器学习/深度学习 算法 文件存储
神经架构搜索NAS详解:三种核心算法原理与Python实战代码
神经架构搜索(NAS)正被广泛应用于大模型及语言/视觉模型设计,如LangVision-LoRA-NAS、Jet-Nemotron等。本文回顾NAS核心技术,解析其自动化设计原理,探讨强化学习、进化算法与梯度方法的应用与差异,揭示NAS在大模型时代的潜力与挑战。
1065 6
神经架构搜索NAS详解:三种核心算法原理与Python实战代码
|
2月前
|
存储 人工智能 算法
从零掌握贪心算法Java版:LeetCode 10题实战解析(上)
在算法世界里,有一种思想如同生活中的"见好就收"——每次做出当前看来最优的选择,寄希望于通过局部最优达成全局最优。这种思想就是贪心算法,它以其简洁高效的特点,成为解决最优问题的利器。今天我们就来系统学习贪心算法的核心思想,并通过10道LeetCode经典题目实战演练,带你掌握这种"步步为营"的解题思维。
|
3月前
|
机器学习/深度学习 资源调度 算法
遗传算法模型深度解析与实战应用
摘要 遗传算法(GA)作为一种受生物进化启发的优化算法,在复杂问题求解中展现出独特优势。本文系统介绍了GA的核心理论、实现细节和应用经验。算法通过模拟自然选择机制,利用选择、交叉、变异三大操作在解空间中进行全局搜索。与梯度下降等传统方法相比,GA不依赖目标函数的连续性或可微性,特别适合处理离散优化、多目标优化等复杂问题。文中详细阐述了染色体编码、适应度函数设计、遗传操作实现等关键技术,并提供了Python代码实现示例。实践表明,GA的成功应用关键在于平衡探索与开发,通过精心调参维持种群多样性同时确保收敛效率
|
3月前
|
机器学习/深度学习 边缘计算 人工智能
粒子群算法模型深度解析与实战应用
蒋星熠Jaxonic是一位深耕智能优化算法领域多年的技术探索者,专注于粒子群优化(PSO)算法的研究与应用。他深入剖析了PSO的数学模型、核心公式及实现方法,并通过大量实践验证了其在神经网络优化、工程设计等复杂问题上的卓越性能。本文全面展示了PSO的理论基础、改进策略与前沿发展方向,为读者提供了一份详尽的技术指南。
粒子群算法模型深度解析与实战应用
|
10月前
|
人工智能 编解码 算法
DeepSeek加持的通义灵码2.0 AI程序员实战案例:助力嵌入式开发中的算法生成革新
本文介绍了通义灵码2.0 AI程序员在嵌入式开发中的实战应用。通过安装VS Code插件并登录阿里云账号,用户可切换至DeepSeek V3模型,利用其强大的代码生成能力。实战案例中,AI程序员根据自然语言描述快速生成了C语言的base64编解码算法,包括源代码、头文件、测试代码和CMake编译脚本。即使在编译错误和需求迭代的情况下,AI程序员也能迅速分析问题并修复代码,最终成功实现功能。作者认为,通义灵码2.0显著提升了开发效率,打破了编程语言限制,是AI编程从辅助工具向工程级协同开发转变的重要标志,值得开发者广泛使用。
8828 71
DeepSeek加持的通义灵码2.0 AI程序员实战案例:助力嵌入式开发中的算法生成革新
|
10月前
|
存储 算法 iOS开发
【狂热算法篇】并查集:探秘图论中的 “连通神器”,解锁动态连通性的神秘力量(通俗易懂版)
【狂热算法篇】并查集:探秘图论中的 “连通神器”,解锁动态连通性的神秘力量(通俗易懂版)
|
存储 缓存 算法
前端算法:优化与实战技巧的深度探索
【10月更文挑战第21天】前端算法:优化与实战技巧的深度探索
225 1