【知识】图论 朱刘算法梳理

news/2024/12/1 18:16:11/文章来源:https://www.cnblogs.com/fanrunze/p/18580138

朱刘算法:

树形图的定义:

  • 以某一个点为根的有向树,被称为 树形图

  • 一个有向图,满足无环且每个点的入度为 \(1\) (除了根节点),被称为 树形图

  • 最小树形图:对于所有树形图中,找到一个总权值和最小的树形图,被称为 最小树形图

最小树形图问题本质上其实就是有向图上的最小生成树问题。 Prim 算法和 Kruskal 算法可以解决无向图上的最小生成树问题。 朱刘算法可以解决有向图上的最小生成树问题。

朱刘算法

朱刘算法是一个迭代算法,每一次迭代:

  1. 除了根节点,对于每个点,找一下这个点的入边中权值最小的边

  2. 判断选出的边中是否存在环

    1. 无环,算法结束

    2. 有环,进入步骤 \(3\)

    3. 将所有环缩点,得到新图 \(G’\),对于每条边:

      • 环内部的边,删去
      • 边的终点 \(u\) 在环内,该边的权值变成原权值减去 \(u\) 在环内的边的权值,即 \(w - w_{环}\)
      • 其他边,不变

算法结束后,之前每一次迭代选出的所有边的总权值之和就是答案。

证明朱刘算法

  • 如果第一次选出的边中不存在环,就意味着当前选出的边满足两个条件,无环且每个点都有一个入边,说明我们选出的是一个树形图,由于每个点选出的边都是所有入边里面权值最小的边,所以一定不可能存在其他方案使得我们选择的边权和更小。

  • 如果有环,那么为什么算法还是对的呢?

假设原图是 \(G\),而缩完点且更新完边权之后的图是 \(G’\)

我们考虑 \(G\) 中任意一个环,由于最终图中一定不能存在环,所以这个环一定存在两个性质,第一点是至少需要去掉一条边,第二点是必然存在一个最优解只去一条边。

假设我们现在任意去掉一条边 \(a\),那么必然会选一条新边 \(b\) 连向 \(a\) 的终点,此时如果我们在任意去掉另外一条边 \(c\),那么必然会再选一条新边 \(d\) 连向 \(c\) 的终点。此时由于 \(c\) 一定小于等于 \(d\),并且由于去掉了 \(a\),选上 \(c\) 并不能让图中存在环且权值会变小,因此我们一定可以把 \(d\) 换回 \(c\)。按照这个原理,任意给我们一个最优解,如果最优解中去掉的边数大于 \(1\),那么我们必然可以从新加的边去掉,换回环上的边,这样它仍然满足是一个树形图,但是总边权和不会变大。由此得出对于任意一个环,必然存在一个最优解只去一条边。

有了以上两个性质,我们可以进行证明。

假设图 \(G\) 中所有环里面只去掉环上一条边的树形图的集合放在左边,将 \(G’\) 里面所有树形图的集合放在右边。

对于左边集合中的图的任何一个环,我们只去掉一条边,然后连上一条新边,由于环已经被我们缩点,那么新边就会连向缩点后的新点,对于新点而言,入边就是唯一的,所以去掉一条边后图中无环且每个点的入度为 \(1\),所以去掉一条边后会构成一个树形图,说明左边集合的任意一个图,我们都可以转化成右边集合的一个树形图。

反过来,对于右边集合中任意一个树形图,我们找到一个不是根节点的缩点后的点,那么这个点必然存在一个入边,且这个入边必然是原图里的某一条边,且它一定连向缩点后这个点内部的某一个点,我们将这个点对应的内部的边去掉,将这条原图中的边加上。这样可以发现,任给我们一个右边集合的树形图,我们都可以转化成左边集合的一个满足两个性质的树形图。

因此两个集合是相互对应的。

然后看一下数量关系,可以发现左边集合加上了一条环外边 \(w\),去掉了一条环内边 \(w’\),因此整个操作等于是加上了 \(w-w’\),而右边集合中我们定义每条边就是 \(w-w’\),所以两个集合在数量关系上也是完全一样的。

综上所述,我们想求左边集合的最小树形图只需要求右边集合的最小树形图就行了,因此每次图中有环进行的处理是正确的。

每次迭代最多去掉一个点,最多迭代 \(n\) 次,每次迭代内部是时间复杂度是 \(O(m)\),因此整个算法时间复杂度是 \(O(nm)\)

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;typedef long long LL;const int N = 105, M = 10005, INF = 1e9;int n, m, rt = 1, X[N], Y[N], col, in[N];int vis[N], id[N], pre[N];struct E{int u, v, w;
} e[M];int inline edmonds() {int ans = 0;while (true) {for (int i = 1; i <= n; i++) in[i] = INF;memset(vis, 0, sizeof vis);memset(id, 0, sizeof id);for (int i = 1; i <= m; i++) if (e[i].w < in[e[i].v]) in[e[i].v] = e[i].w, pre[e[i].v] = e[i].u;for (int i = 1; i <= n; i++)if (in[i] == INF && i != rt) return -1;col = 0;for (int i = 1; i <= n; i++) {if (i == rt) continue;ans += in[i];int v = i;while (!vis[v] && !id[v] && v != rt)vis[v] = i, v = pre[v];if (v != rt && vis[v] == i) {id[v] = ++col;for (int x = pre[v]; x != v; x = pre[x]) id[x] = col;}}if (!col) break;for (int i = 1; i <= n; i++) if (!id[i]) id[i] = ++col;int tot = 0;for (int i = 1; i <= m; i++) {int a = id[e[i].u], b = id[e[i].v];if (a == b) continue;e[++tot] = (E) { a, b, e[i].w - in[e[i].v] };}m = tot, n = col, rt = id[rt];}return ans;
}int main() {scanf("%d%d%d", &n, &m, &rt);int tot = 0;for (int i = 1; i <= m; i++) {int a, b, c; scanf("%d%d%d", &a, &b, &c);if (b != rt && a != b) e[++tot] = (E) { a, b, c };}m = tot;printf("%d\n", edmonds());return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

《智能汽车传感器:原理设计应用》新书推荐

《智能汽车传感器:原理设计应用》新书推荐 由化学工业出版社资深编辑张海丽老师负责策划编辑。本书在京东、淘宝天猫、当当网上均有销售京东:https://search.jd.com/Search?keyword=%E6%99%BA%E8%83%BD%E6%B1%BD%E8%BD%A6%E4%BC%A0%E6%84%9F%E5%99%A8%EF%BC%9A%E5%8E%9F%E7%…

38. html_02

1. 标签的id属性和class属性 id 用于精确查找某个标签。 类似于标签的唯一标识符,用于在同一个页面上唯一标识一个特定的元素。每个id值在整个文档中都必须是唯一的,不能重复使用。通过id值,可以在JavaScript或CSS中引用特定标签,并对其进行操作或样式化。 class 类似于面向…

高级语言程序设计课程第九次个人作业(102400106刘鑫语)

这个作业属于哪个课程:https://edu.cnblogs.com/campus/fzu/2024C/ 这个作业要求在哪里: https://edu.cnblogs.com/campus/fzu/2024C/homework/13311 学号:102300106 姓名:刘鑫语 14.17.3;14.17.4 结构模板和数组,没有什么问题 14.17.5一开始定义days函数中试图访问month…

分布式训练

模型并行与数据并行 Parameter Server 同步更新: 在 work 比较多的情况下,parameter server 承受的压力会比较大,网络开销也大 异步更新: 1参数和更新用的梯度并不来自同一个迭代。用来更新的梯度可能是几步更新前的参数算出来的。 2参数的读取并没有加锁。这导致 worker 可…

宝塔安装thinkphp低版本路径不对

如果你在宝塔面板上安装了ThinkPHP低版本(例如ThinkPHP 5.0或更早版本),但遇到了路径问题,可以按照以下步骤进行排查和解决: 1. 检查网站根目录设置 确保你的网站根目录设置正确。通常,ThinkPHP项目的入口文件是 public 目录下的 index.php 文件。登录宝塔面板。 进入“网…

C#基础之集合讲解

目录1 集合1.1 数组1.1.1 简介1.1.2 声明使用1.1.2.1 声明 & 初始化1.1.2.2 赋值给数组1.1.2.3 访问数组元素1.1.3 多维数组1.1.3.1 声明1.1.3.2 初始化二维数组1.1.3.3 访问二维数组元素1.1.4 交错数组1.1.5 传递数组给函数1.1.6 Array1.1.6.1 简介1.1.6.2 属性1.1.6.3 方…

织梦网站关键词修改,如何优化DedeCMS的关键词设置

修改织梦网站(DedeCMS)的关键词设置可以通过以下步骤实现:登录管理后台:使用管理员账号登录DedeCMS管理后台。 进入SEO设置:导航至“系统” > “系统基本参数” > “SEO设置”。 修改关键词:在“关键词”字段中,输入新的关键词。 保存更改:确认无误后,保存修改。…

C#基础之不安全代码讲解

目录1 不安全代码1.1 简介1.2 指针变量1.3 编译不安全代码1.4 使用示例1.4.1 简单使用1.4.2 使用指针检索数据值1.4.3 传递指针作为方法的参数1.4.4 使用指针访问数组元素1.5 fixed1.5.1 为什么需要 fixed1.5.2 为什么 int *p 和 int[] p 是不同的类型1.5.3 示例1.6 stackalloc…

IDA+WSL2实现本地linux动态调试

1、首先在ida安装目录找到dbgsrv这个文件夹,打开后把“linux_server”这个文件拖到你的linux中(我放在/root位置)2、然后赋予两个文件权限(linux-server和要调试的文件) chmod +x /root/linux_server chmod +x 你的待调试文件位置然后运行调试组件 /root/linux_server64参…

英特尔固态硬盘维修数据恢复

固态硬盘(SSD)在维修和数据恢复方面,需要根据具体情况采取不同的方法。以下是一些常见的固态硬盘维修和数据恢复方法: 一、检查硬件连接与电源 检查连接线: 打开计算机主机箱,检查SATA或NVMe接口的连接线是否插紧,是否有松动或断裂。 重新插拔连接线,然后重启计算机,看…

WEB AK赛-web2_观星

一看就是sql注入题 尝试了一下发现过滤的东西还挺多的(union,like,=,’,空格,and,if,逗号,ascii,sleep)等 不能用正常的布尔盲注:1^if(ascii(substr(flag,1,1))=104,1,0) 题目过滤了if 看了wp才知道有这种方式:case(A)when(B)then(C)else(D)end 绕过过滤: 过滤了空格可以用(…

【学校训练记录】12月个人训练赛1个人题解

A对于n本书拿出k本较为难实现,但是从n本书里拿出n-k本就容易多了 对于n本书里拿一本为特殊情况,不管怎么拿都为0 对于n本书里拿n-k本的话,我们假设拿的最后一本为i那么 他就是拿出n-k-1本书的情况再加上拿出第i本的情况 其中差值变化为拿出n-k-1本书的值,加上我abs(w[i]-w[…