首页 > 其他分享 >最小生成树

最小生成树

时间:2023-12-23 17:11:57浏览次数:27  
标签:int Kruskal 最小 生成 edge include

最小生成树

前置知识

  • 并查集

  • 图论

概念

条件

最小生成树的满足条件为:

  • 在无向图中选取总权值最少的边让所有点连通。

  • 要求结果是一棵树,边数比点数少 \(1\)。

当然,最小生成树的结果可能不唯一。

特性

  • 图中任意一条非树边都会和树边构成一个环。
  • 非树边一定是环中最大的边。否则可以替换掉最大的边,得到一个更小生成树。

其他

最小生成树有两种算法:Kruskal 和 Prim。

Kruskal 是在并查集的基础上展开。

Prim 是在最短路的基础上展开。

Kruskal

我们可以从选边的角度来构造生成树,通过点的连通性来对边的选取进行判断。

我们按权值由小到大选择不成环的边加入树中。(由树的特性可知,成环时该边一定是环中最大的边,不应该选取)。如果选出的边比点数少 \(1\),说明最小生成树存在,否则不存在。

那么如何快速的判断是否成环?我们可以使用并查集来合并点及判断点的连通性。

步骤

  1. 读入所有边并初始化并查集(\(f_i = i\))。

  2. 按权值排序。

  3. 合并所有边并计算答案。

  4. 输出。

Prim

可以从选点的角度来构造生成树,不断将新的点拉入生成树中。

步骤

  1. 读入所有边。

  2. 把所有点的距离设为 \(\infty\),并将任意一点距离设为 \(0\)。

  3. 寻找没有进入生成树且距离最小的点进入生成树并累加距离到答案。

  4. 更新相邻点距离。

  5. 重复 \(3, 4\) 步直到所有点进入生成树。

  6. 输出。

例题 1(洛谷 P3366 【模板】最小生成树)

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

原题:https://www.luogu.com.cn/problem/P3366

【Kruskal】

代码是以前写的,可能不好看。

#include <bits/stdc++.h>
using namespace std;
#define qwq ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
const int N = 200000 + 20, M = 5000 + 50;

struct node {
  int x, y, z;
  friend bool operator<(const node &a, const node &b) {
    return a.z < b.z;
  }
};

node edge[N];
int n, m, x, y, z, f[M], l[M], ans = 0;
bool b = 1;

int find(int i) {
  if (f[i] == i)
    return i;
  return f[i] = find(f[i]);
}

void merge(int u, int v) {
  u = find(u), v = find(v);
  if (l[u] > l[v]) {
    swap(l[u], l[v]);
  }
  f[u] = v;
  l[v] += l[u];
}

int main() {
  qwq;
  cin >> n >> m;
  for (int i = 1; i <= n; ++i) {
    f[i] = i, l[i] = 1;
  }
  for (int i = 1; i <= m; ++i) {
    cin >> edge[i].x >> edge[i].y >> edge[i].z;
  }
  sort(edge + 1, edge + m + 1);
  for (int i = 1; i <= m; ++i) {
    if (find(edge[i].x) == find(edge[i].y))
      continue;
    merge(edge[i].x, edge[i].y);
    ans += edge[i].z;
    if (l[find(edge[i].x)] == n || l[find(edge[i].y)] == n) {
      b = 0;
      break;
    }
  }
  if (b)
    cout << "orz";
  else
    cout << ans;
  return 0;
}

【Prim】

借鉴一下老师写的,我写的 Kruskal。

例题 2(洛谷 P1550 [USACO08OCT] Watering Hole G)

Farmer John 决定将水引入到他的 \(n\) 个农场。他准备通过挖若干井,并在各块田中修筑水道来连通各块田地以供水。在第 \(i\) 号田中挖一口井需要花费 \(W_i\) 元。连接 \(i\) 号田与 \(j\) 号田需要 \(P_{i,j}\)(\(P_{j,i}=P_{i,j}\))元。

每次输入 \(w_i\) 时,把 \(i\) 和 \(n + 1\) 连接,边权为 \(w_i\)。

然后在输入 \(p\) 数组后,套一个双层循环,如果 \(i \neq j\),把 \(i\) 和 \(j\) 连接
,边权为 \(p_{i, j}\)。

存完边之后按照边权排序。排序后跑一遍 Kruskal 即可。

#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <queue>

using namespace std;

using ll = long long;

#define mtest for (cin >> t; t; -- t)

const int kMaxN = 1e5 + 10, kMaxM = 1010, kInf = (((1 << 30) - 1) << 1) + 1;
const ll kLInf = 9.22e18;

int n, w[kMaxN], f[kMaxN], p[kMaxM][kMaxM], ans = 0;

struct node {
  int u, v, w;
} e[kMaxN];

int F(int x) {
  return f[x] == x? x : f[x] = F(f[x]);
}

bool cmp(node a, node b) {
  return a.w < b.w; // 注意是 <
}

void U(int u, int v, int w) {
  int x = F(u), y = F(v);
  if (x != y) {
    f[f[u]] = f[v];
    ans += w;
  }
}

int main() {
  ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
  cin >> n;
  for (int i = 1; i - 1 <= n; ++ i) { // 初始化
    f[i] = i;
  }
  int id = 0;
  for (int i = 1; i <= n; ++ i) { // 村边
    e[++ id].u = i;
    e[id].v    = n + 1;
    // i & n + 1 连接
    cin >> e[id].w;
  }
  for (int i = 1; i <= n; ++ i) {
    for (int j = 1; j <= n; ++ j) {
      cin >> p[i][j];
    }
  }
  for (int i = 1; i <= n; ++ i) {
    for (int j = 1; j <= n; ++ j) {
      if (i == j) {
        continue;
      }
      e[++ id].u = i;
      e[id].v = j;
      // i & j 连接
      e[id].w = p[i][j];
    }
  }
  sort(e + 1, e + id + 1, cmp); // 按边权排序
  for (int i = 1; i <= id; ++ i) { // 跑 Kruskal 
    U(e[i].u, e[i].v, e[i].w); 
  }
  cout << ans << '\n';
  return 0;
}

习题

  1. 洛谷 P1194 / P1195 / P1396 / P2330 / P2700 / P4047。

  2. 自行在 CF / AT 上面找关于最小生成树的题。

标签:int,Kruskal,最小,生成,edge,include
From: https://www.cnblogs.com/bc2qwq/p/MinimumSpanningTree.html

相关文章

  • 一款基于.NET Core的快速开发框架、支持多种前端UI、内置代码生成器
    前言经常看到有小伙伴在技术群里问有没有什么好用且快速的开发框架推荐的,今天就给大家分享一款基于MITLicense协议开源、免费的.NETCore快速开发框架、支持多种前端UI、内置代码生成器、一款高效开发的利器:WalkingTec.Mvvm框架(简称WTM)。官方项目介绍WalkingTec.Mvvm框架(简称W......
  • 随机幸运号码自动生成器之Python宝典【上】
    一、前言需求背景描述前面我编写了一段能生成随机幸运号码的代码,但是并不实用,每次去买颜色艳丽的票之前都需要在PyCharm上运行并将幸运号码在控制台打印出来为解决这个问题,尝试使用Python的ttkbootstrap实现简单的号码展示,并根据当前日期展现对应类型(超级彩票、彩色球票)的幸运号码,......
  • Github Copilot生成代码和单元测试并执行
    ChatGPTPrompts整理总结 最近一直在学习ChatGPTPrompt的编写技巧,做了一些验证和整理,分享给大家ActasaLinuxTerminal英文PromptIwantyoutoactasalinuxterminal.Iwilltypecommandsandyouwillreplywithwhattheterminalshouldshow.Iwantyouto......
  • 倾斜三维模型生成DOM、DSM(附下载软件)
    1、准备数据倾斜三维模型数据2、加载瓦片打开Dasviewer软件3、按范围选择瓦片如果数据太大需要对数据进行部分选择,可以用这个步骤左键双击结束导出选择瓦片,会生成新的瓦片文件夹4、导出DOM、DSM根据需求设置参数结果软件下载地址https://www.daspatial.com/cn/download......
  • PHP医院手术麻醉系统源码,与HIS系统无缝对接,自动采集相关数据,生成医疗文书
    手术麻醉系统源码包括两大部分,手术管理和麻醉管理。1. 手术管理手术管理主要包括手术申请、手术安排、术中相关工作、手术室相关工作。手术安排:手术室安排、手术护士安排等。术中相关工作:器械清点、术中护理记录等。手术室相关工作:人员排班、工作量统计、手术时间统计等。同时,还可......
  • 文档生成工具:Linux下doxygen的使用
    一、概述Doxygen是一个代码文档生成工具。它从代码文件中提取注释并可生成多种文档形式。如:网页文档HTML,RTF(MS-Word),PDF等等。同时也可生成函数之间的调用和文件的依赖关系图表。二、安装平台:linuxsudoapt-getinstalldoxygensudoapt-getinstallgraphvizsudoapt-ge......
  • [问题记录] C# 使用NPOI操作Excel模版写入数据 - 生成文件打开时提示 "发现 XXX.xlsx
    解决方案:1.先确保原来的模版文件打开是正常的,没有提示要恢复2.用Office打开这个模版文件,另存为一个文件。用这个文件来作为模版使用。 问题描述:使用C#NPOI操作Excel模版(模版用office打开是正常的),写入数据,导出的文件打开时提示是否尝试恢复,点击“是”后,发现Excel内......
  • 如果你希望打包的Python脚本在运行时不显示命令行窗口,你可以在使用`auto-py-to-exe`进
    auto-py-to-exe是一个基于Eel和PyInstaller构建的工具,可以通过简单的UI界面将Python项目中的.py文件打包为.exe文件¹。以下是使用auto-py-to-exe的步骤:环境要求:Python环境需要大于或等于2.7¹。模块安装:在命令行中输入以下命令来安装auto-py-to-exe¹:pipinstallauto-py-to-exe或......
  • 生成式AI:未来的发展方向是什么?
    生成式AI的问世标志着人工智能领域迎来了一个全新时代的开启。今年,ChatGPT的面世引起了广泛的热议和关注,许多人认为这标志着人工智能领域进入了一个大规模探索的时代。然而,事实上,这只是生成式AI发展的第一波浪潮,第二波浪潮已经悄然兴起,即整合时代。在这个时代,不同的生成式AI系统和......
  • 使用代码生成工具快速开发应用-结合后端Web API提供接口和前端页面快速生成,实现通用的
    在前面随笔《在Winform应用中增加通用的业务编码规则生成》,我介绍了基于Winform和WPF的一个通用的业务编码规则的管理功能,本篇随笔介绍基于后端WebAPI接口,实现快速的Vue3+ElementPlus前端界面的开发整合,同样是基于代码生成工具实现快速的前端代码的生成处理。1、通用的业务编码......