• 图论进阶之路-最小生成树模版


    在学习最小生成树之前,我们首先得掌握写最小生成树的必要知识:

    并查集

    主要用于判断两个结点是否在一个集合内

    1. 并查集模版--------------------------------------------------------------------
    2. static class UnionFind {
    3. int[] father;//记录祖先
    4. int[] size;//记录集合大小
    5. LinkedList stack = new LinkedList<>();//用于扁平化处理
    6. UnionFind(int n,int[][] edges){
    7. father = new int[n];
    8. size = new int[n];
    9. for(int i=0;i
    10. father[i] = i;//初始化先将所有祖先设置为自己
    11. }
    12. Arrays.fill(size,1);
    13. }
    14. int find(int n){
    15. while(father[n] != n){//不断查找祖先,直到祖先就是自己
    16. stack.push(n);
    17. n = father[n];
    18. }
    19. while(!stack.isEmpty()){
    20. father[stack.pop()] = n;//扁平化处理,方便下一次查找
    21. }
    22. return n;//返回祖先
    23. }
    24. void union(int a,int b){//找到两个结点的祖先,将较小的的集合合并到较大的集合下
    25. int x = find(a);
    26. int y = find(b);
    27. if(size[x] > size[y]){
    28. father[y] = x;
    29. size[x] += size[y];
    30. }else{
    31. father[x] = y;
    32. size[y] += size[x];
    33. }
    34. }
    35. }

    大家可以当做模版记忆,有了并查集后,我们就可以判断任意两个结点是否联通

    接下来,便可以应对最小生成树的问题:

    题目描述

    如题,给出一个无向图,求出最小生成树,如果该图不连通,则输出 orz

    输入格式

    第一行包含两个整数 𝑁,𝑀N,M,表示该图共有 𝑁N 个结点和 𝑀M 条无向边。

    接下来 𝑀M 行每行包含三个整数 𝑋𝑖,𝑌𝑖,𝑍𝑖Xi​,Yi​,Zi​,表示有一条长度为 𝑍𝑖Zi​ 的无向边连接结点 𝑋𝑖,𝑌𝑖Xi​,Yi​。

    输出格式

    如果该图连通,则输出一个整数表示最小生成树的各边的长度之和。如果该图不连通则输出 orz

    输入输出样例

    输入 #1复制

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

    输出 #1复制

    7

    说明/提示

    数据规模:

    对于 20%20% 的数据,𝑁≤5N≤5,𝑀≤20M≤20。

    对于 40%40% 的数据,𝑁≤50N≤50,𝑀≤2500M≤2500。

    对于 70%70% 的数据,𝑁≤500N≤500,𝑀≤104M≤104。

    对于 100%100% 的数据:1≤𝑁≤50001≤N≤5000,1≤𝑀≤2×1051≤M≤2×105,1≤𝑍𝑖≤1041≤Zi​≤104。

    样例解释:

    所以最小生成树的总边权为 2+2+3=72+2+3=7。

    最短生成树的边一定是 n-1 条,若小于 n-1 条则无法保证可以连接所有结点,若大于 n-1 条则无法保证生成的树是最小的

    这是一道典型的最小生成树模版题,最短生成树的思路其实很简单,就是不断拿到权值最小的边,判断两端结点是否在一个集合内,若不在,证明该边是最短生成树的一条边,其中难点就在于如何判断两端结点是否在一个集合内,但是,在学习了并查集后这个难点也变得唾手可得

    我们直接套用并查集模版解决

    最小生成树

    1. import java.util.*;
    2. public class Main {
    3. public static void main(String[] args) {
    4. Scanner sc = new Scanner(System.in);
    5. int n = sc.nextInt();
    6. int m = sc.nextInt();
    7. List<int[]> power = new ArrayList<>();//记录所有权值以及边连接的结点
    8. UnionFind uf = new UnionFind(n);
    9. for(int i=0;i
    10. int a = sc.nextInt(),b = sc.nextInt(),p = sc.nextInt();
    11. power.add(new int[]{p,a,b});
    12. }
    13. //按照权值从小到大生成树,若遍历到的边使得生成树出现环,就不添加该边
    14. Collections.sort(power,(a,b)->a[0]-b[0]);
    15. long ans = 0;
    16. int count = 0;
    17. for(int[] a : power){
    18. if(uf.find(a[1]) != uf.find(a[2])){
    19. uf.union(a[1], a[2]);
    20. ans += a[0];
    21. count++;
    22. }
    23. }
    24. System.out.println(count == n-1 ? ans : "orz");
    25. }
    26. static class UnionFind{
    27. int[] father;
    28. int[] size;
    29. LinkedList stack;
    30. UnionFind(int n){
    31. father = new int[n+1];
    32. for(int i=1;i<=n;i++)father[i] = i;
    33. size = new int[n+1];
    34. stack = new LinkedList<>();
    35. }
    36. int find(int x){
    37. while(father[x] != x){
    38. stack.push(x);
    39. x = father[x];
    40. }
    41. //扁平化处理
    42. while(!stack.isEmpty()){
    43. father[stack.pop()] = x;
    44. }
    45. return x;
    46. }
    47. void union(int x,int y){
    48. int fx = find(x);
    49. int fy = find(y);
    50. if(size[fx] > size[fy]){
    51. father[fy] = fx;
    52. size[fx] += size[fy];
    53. }else{
    54. father[fx] = fy;
    55. size[fy] += size[fx];
    56. }
    57. }
    58. }
    59. }

    由于这是洛谷的 OJ 对 java 玩家不是很友好,代码虽然是对的,但是内存只给128MB,有三个样例会超出一点内存限制,我用GPT换成了C++语言,这个交上去就会全通过了,不过大家可以放心,在比赛和笔试的时候是不会出现这种情况的,出题人会把语言局限考虑进去的

    1. #include
    2. #include
    3. #include
    4. using namespace std;
    5. class Main {
    6. public:
    7. vector<int> father;
    8. vector<int> size;
    9. vector<int> stack;
    10. Main(int n) {
    11. father.resize(n + 1);
    12. for (int i = 1; i <= n; i++) father[i] = i;
    13. size.resize(n + 1);
    14. }
    15. int find(int x) {
    16. while (father[x] != x) {
    17. stack.push_back(x);
    18. x = father[x];
    19. }
    20. while (!stack.empty()) {
    21. father[stack.back()] = x;
    22. stack.pop_back();
    23. }
    24. return x;
    25. }
    26. void Union(int x, int y) {
    27. int fx = find(x);
    28. int fy = find(y);
    29. if (size[fx] > size[fy]) {
    30. father[fy] = fx;
    31. size[fx] += size[fy];
    32. } else {
    33. father[fx] = fy;
    34. size[fy] += size[fx];
    35. }
    36. }
    37. };
    38. int main() {
    39. int n, m;
    40. cin >> n >> m;
    41. vectorint>> power;
    42. Main uf(n);
    43. for (int i = 0; i < m; i++) {
    44. int a, b, p;
    45. cin >> a >> b >> p;
    46. power.push_back({p, a, b});
    47. }
    48. sort(power.begin(), power.end(), [](const vector<int>& a, const vector<int>& b) {
    49. return a[0] < b[0];
    50. });
    51. long long ans = 0;
    52. int count = 0;
    53. for (const auto& a : power) {
    54. if (uf.find(a[1]) != uf.find(a[2])) {
    55. uf.Union(a[1], a[2]);
    56. ans += a[0];
    57. count++;
    58. }
    59. }
    60. if(count == n - 1){
    61. cout << ans << endl;
    62. }else{
    63. cout << "orz" << endl;
    64. }
    65. return 0;
    66. }

  • 相关阅读:
    python---生成器表达式
    稳压二极管的应用及注意事项
    Python的基础语法(十四)
    果断收藏!考完PMP还能学什么?一文解答你的疑惑
    卡数据兼容性要求-consumer设备架构
    数据结构--字典树(trie)
    C#开发的OpenRA游戏之世界存在的属性GivesExperience(4)
    Go 语言 设计模式-桥接模式
    leetcode23 合并K个有序列表
    从JDK8到JDK17的新特性
  • 原文地址:https://blog.csdn.net/m0_75138009/article/details/138139636