【图轮】【 最小生成树】【 并集查找】1489. 找到最小生成树里的关键边和伪关键边

本文涉及知识点

图轮 最小生成树 并集查找 关键边

1489. 找到最小生成树里的关键边和伪关键边

给你一个 n 个点的带权无向连通图,节点编号为 0 到 n-1 ,同时还有一个数组 edges ,其中 edges[i] = [fromi, toi, weighti] 表示在 fromi 和 toi 节点之间有一条带权无向边。最小生成树 (MST) 是给定图中边的一个子集,它连接了所有节点且没有环,而且这些边的权值和最小。
请你找到给定图中最小生成树的所有关键边和伪关键边。如果从图中删去某条边,会导致最小生成树的权值和增加,那么我们就说它是一条关键边。伪关键边则是可能会出现在某些最小生成树中但不会出现在所有最小生成树中的边。
请注意,你可以分别以任意顺序返回关键边的下标和伪关键边的下标。
示例 1:
在这里插入图片描述

输入:n = 5, edges = [[0,1,1],[1,2,1],[2,3,2],[0,3,2],[0,4,3],[3,4,3],[1,4,6]]
输出:[[0,1],[2,3,4,5]]
解释:上图描述了给定图。
下图是所有的最小生成树。
在这里插入图片描述

注意到第 0 条边和第 1 条边出现在了所有最小生成树中,所以它们是关键边,我们将这两个下标作为输出的第一个列表。
边 2,3,4 和 5 是所有 MST 的剩余边,所以它们是伪关键边。我们将它们作为输出的第二个列表。
示例 2 :

在这里插入图片描述

输入:n = 4, edges = [[0,1,1],[1,2,1],[2,3,1],[0,3,1]]
输出:[[],[0,1,2,3]]
解释:可以观察到 4 条边都有相同的权值,任选它们中的 3 条可以形成一棵 MST 。所以 4 条边都是伪关键边。

提示:
2 <= n <= 100
1 <= edges.length <= min(200, n * (n - 1) / 2)
edges[i].length == 3
0 <= fromi < toi < n
1 <= weighti <= 1000
所有 (fromi, toi) 数对都是互不相同的。

最小生成树

n是点数,e是边数。

按边加

按边的权重由低到高排序。依次处理各边 n 1 ↔ n 2 n1 \leftrightarrow n2 n1n2
{ 忽略 n 1 , n 2 已经连接 ( 1 ) 加到生成树中 n 1 , n 2 未连接 ( 2 ) \begin{cases} 忽略 && n1,n2已经连接 && (1) \\ 加到生成树中 &&n1,n2未连接 && (2)\\ \end{cases} {忽略加到生成树中n1,n2已经连接n1,n2未连接(1)(2)
情况(1): n1 ↔ \leftrightarrow n2 说明 n1到n2存在环 ,删除环上任意边都不影响连通性,而 n 1 ↔ n 2 n1 \leftrightarrow n2 n1n2最长,故删除它。
情况(2) 令n1当前所在的连通区域为r1,则r1中的点有且只有一个点会和r1外的点连接(待证一)。令其为n3和n4。 n 3 ↔ n 4 换成 n 1 ↔ n 2 n3 \leftrightarrow n4 换成n1 \leftrightarrow n2 n3n4换成n1n2 更短。
待证一:如果没有边,则n1无法与n2连通;如果有两条边,会形成环。
时间复杂度:O(eloge) 排序,并集查找如果用启发式合并,也是O(nlogn)。

按点加

点分为两个点集S和T,S集只包括任意一个点,T集包括其它点。n1 ∈ \in S,n2 ∈ \in T ,寻找最短的 n 1 ↔ n 2 n1 \leftrightarrow n2 n1n2
将n2加到S, n 1 ↔ n 2 n1 \leftrightarrow n2 n1n2 加到最小生成树。更新T中各点到S的最短距离,只需要更新T中各点到n2的距离。
证明:
当前S T不连通,如果不选择 n 1 ↔ n 2 n1 \leftrightarrow n2 n1n2 ,只能选择更长的边。
时间复杂度: O(nn)

题解

删除某条边会,最小生成树不存在或变大,是关键边。
把某条边加到最小生成树后,余下的边继续生成最小关键树,权值不变是伪关键边。

封装库

class CUnionFindMST
{
public:CUnionFindMST(const int iNodeSize) :m_uf(iNodeSize){}void AddEdge(const int iNode1, const int iNode2, int iWeight){if (m_uf.IsConnect(iNode1, iNode2)){return;}m_iMST += iWeight;m_uf.Union(iNode1, iNode2);}void AddEdge(const vector<int>& v){AddEdge(v[0], v[1], v[2]);}int MST(){if (m_uf.GetConnetRegionCount() > 1){return -1;}return m_iMST;}
private:int m_iMST = 0;CUnionFind m_uf;
};class CNearestMST
{
public:CNearestMST(const int iNodeSize) :m_bDo(iNodeSize), m_vDis(iNodeSize, INT_MAX), m_vNeiTable(iNodeSize){}void Init(const vector<vector<int>>& edges){for (const auto& v : edges){Add(v);}}void Add(const vector<int>& v){m_vNeiTable[v[0]].emplace_back(v[1], v[2]);m_vNeiTable[v[1]].emplace_back(v[0], v[2]);}int MST(int start){int next = start;while ((next = AddNode(next)) >= 0);return m_iMST;}int MST(int iNode1, int iNode2, int iWeight){m_bDo[iNode1] = true;for (const auto& it : m_vNeiTable[iNode1]){if (m_bDo[it.first]){continue;}m_vDis[it.first] = min(m_vDis[it.first], (long long)it.second);}m_iMST = iWeight;int next = iNode2;while ((next = AddNode(next)) >= 0);return m_iMST;}private:int AddNode(int iCur){m_bDo[iCur] = true;for (const auto& it : m_vNeiTable[iCur]){if (m_bDo[it.first]){continue;}m_vDis[it.first] = min(m_vDis[it.first], (long long)it.second);}int iMinIndex = -1;for (int i = 0; i < m_vDis.size(); i++){if (m_bDo[i]){continue;}if ((-1 == iMinIndex) || (m_vDis[i] < m_vDis[iMinIndex])){iMinIndex = i;}}if (-1 != iMinIndex){if (INT_MAX == m_vDis[iMinIndex]){m_iMST = -1;return -1;}m_iMST += m_vDis[iMinIndex];}return iMinIndex;}vector<bool> m_bDo;vector<long long> m_vDis;vector < vector<std::pair<int, int>>> m_vNeiTable;long long m_iMST = 0;
};

代码

class Solution {
public:vector<vector<int>> findCriticalAndPseudoCriticalEdges(int n, vector<vector<int>>& edges) {m_c = edges.size();vector<int> indexs;for (int i = 0; i < m_c; i++){indexs.emplace_back(i);}std::sort(indexs.begin(), indexs.end(), [&](const int& i1, const int& i2){return edges[i1][2] < edges[i2][2];});int iMST = 0;{CNearestMST mst(n);mst.Init(edges);iMST = mst.MST();}vector<vector<int>> vRet(2);for (int i = 0; i < m_c; i++){//关键边			{auto tmp = edges;tmp.erase(tmp.begin() + indexs[i]);CNearestMST mst1(n);mst1.Init(tmp);const int iMST1 = mst1.MST();if ((-1 == iMST1) || (iMST1 > iMST)){vRet[0].emplace_back(indexs[i]);continue;}}{CUnionFindMST mst2(n);mst2.AddEdge(edges[indexs[i]]);for (int j = 0; j < m_c; j++){if (j == i){continue;}const auto& v = edges[indexs[j]];mst2.AddEdge(v);}const int iMST2 = mst2.MST();if (iMST2 == iMST){vRet[1].emplace_back(indexs[i]);}}}std::sort(vRet[0].begin(), vRet[0].end());std::sort(vRet[1].begin(), vRet[1].end());return vRet;}int m_c;
};

2023年4月版1

class Solution {
public:
vector<vector> findCriticalAndPseudoCriticalEdges(int n, vector<vector>& edges) {
m_c = edges.size();
vector indexs;
for (int i = 0; i < m_c; i++)
{
indexs.emplace_back(i);
}
std::sort(indexs.begin(), indexs.end(), [&](const int& i1, const int& i2 )
{
return edges[i1][2] < edges[i2][2];
});
int iMST = 0;
{
CUnionFindMST mst(n);
for (int i = 0; i < m_c; i++)
{
const auto& v = edges[indexs[i]];
mst.AddEdge(v);
}
iMST = mst.MST();
}
vector<vector> vRet(2);
for (int i = 0; i < m_c; i++)
{
//关键边
{
CUnionFindMST mst1(n);
for (int j = 0; j < m_c; j++)
{
if (j == i)
{
continue;
}
const auto& v = edges[indexs[j]];
mst1.AddEdge(v);
}
const int iMST1 = mst1.MST();
if ((-1 == iMST1) || (iMST1 > iMST))
{
vRet[0].emplace_back(indexs[i]);
continue;
}
}

		{CUnionFindMST mst2(n);mst2.AddEdge(edges[indexs[i]]);for (int j = 0; j < m_c; j++){if (j == i){continue;}const auto& v = edges[indexs[j]];mst2.AddEdge(v);}const int iMST2 = mst2.MST();if (iMST2 == iMST){vRet[1].emplace_back(indexs[i]);}}}return vRet;
}
int m_c;

};

2023年4月版2

class Solution {
public:
vector<vector> findCriticalAndPseudoCriticalEdges(int n, vector<vector>& edges) {
m_c = edges.size();
vector indexs;
for (int i = 0; i < m_c; i++)
{
indexs.emplace_back(i);
}
std::sort(indexs.begin(), indexs.end(), [&](const int& i1, const int& i2)
{
return edges[i1][2] < edges[i2][2];
});
int iMST = 0;
{
CNearestMST mst(n);
mst.Init(edges);
iMST = mst.MST();
}
vector<vector> vRet(2);
for (int i = 0; i < m_c; i++)
{
//关键边
{
auto tmp = edges;
tmp.erase(tmp.begin() + indexs[i]);
CNearestMST mst1(n);
mst1.Init(tmp);
const int iMST1 = mst1.MST();
if ((-1 == iMST1) || (iMST1 > iMST))
{
vRet[0].emplace_back(indexs[i]);
continue;
}
}
{
CUnionFindMST mst2(n);
mst2.AddEdge(edges[indexs[i]]);
for (int j = 0; j < m_c; j++)
{
if (j == i)
{
continue;
}
const auto& v = edges[indexs[j]];
mst2.AddEdge(v);
}
const int iMST2 = mst2.MST();
if (iMST2 == iMST)
{
vRet[1].emplace_back(indexs[i]);
}
}
}
std::sort(vRet[0].begin(), vRet[0].end());
std::sort(vRet[1].begin(), vRet[1].end());
return vRet;
}
int m_c;
};

通过重边、割边判断

class Solution{
public:vector<vector<int>> findCriticalAndPseudoCriticalEdges(int n, vector<vector<int>>&edges) {std::map<int, vector<int>> mWeightToEdgeIndexs;for (int i = 0; i < edges.size(); i++){mWeightToEdgeIndexs[edges[i][2]].emplace_back(i);}CUnionFind uf(n);vector<vector<int>> vRet(2);for (const auto& it : mWeightToEdgeIndexs){CNeiBo2 neiBo(n, false, 0);std::unordered_map<int, std::unordered_map<int,int>> mRepeateEdge;for (const auto index : it.second){const int n1 = edges[index][0];const int n2 = edges[index][1];if (uf.IsConnect(n1, n2)){continue;}const int iRegion1 = uf.GetConnectRegionIndex(n1);		const int iRegion2 = uf.GetConnectRegionIndex(n2);neiBo.Add(iRegion1, iRegion2);mRepeateEdge[iRegion1][iRegion2]++;mRepeateEdge[iRegion2][iRegion1]++;}CCutEdge cutEdge(neiBo.m_vNeiB);for (const auto index : it.second){const int n1 = edges[index][0];const int n2 = edges[index][1];if (uf.IsConnect(n1, n2)){continue;}const int iRegion1 = uf.GetConnectRegionIndex(n1);const int iRegion2 = uf.GetConnectRegionIndex(n2);if (mRepeateEdge[iRegion1][iRegion2] > 1){//重边无论是否是环,都不是关键边vRet[1].emplace_back(index);}else if (cutEdge.IsCut(iRegion1,iRegion2) || cutEdge.IsCut(iRegion2, iRegion1)){vRet[0].emplace_back(index);}else{vRet[1].emplace_back(index);}}	for (const auto index : it.second){const int n1 = edges[index][0];const int n2 = edges[index][1];uf.Union(n1, n2);}}return vRet;}
};

扩展阅读

视频课程

有效学习:明确的目标 及时的反馈 拉伸区(难度合适),可以先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771

如何你想快速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.csdn.net/lecturer/6176

相关

下载

想高屋建瓴的学习算法,请下载《喜缺全书算法册》doc版
https://download.csdn.net/download/he_zhidan/88348653

我想对大家说的话
闻缺陷则喜是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。
如果程序是一条龙,那算法就是他的是睛

测试环境

操作系统:win7 开发环境: VS2019 C++17
或者 操作系统:win10 开发环境: VS2022 C++17
如无特殊说明,本算法用**C++**实现。

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

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

相关文章

书生·浦语全链路开源开放体系 第二期

文章目录 大模型背景大模型开发流程InternLM 2.0SFT与RLHFInternLM2主要亮点 书生浦语全链路开源开放体系数据-书生万卷InternLM-Train微调 XTuner评测工具 OpenCompass部署 LMDeploy智能体 Lagent智能体工具箱 AgentLego 大模型背景 专用模型&#xff1a;针对特定的任务&…

【Web】NSSCTF Round#20 Basic 个人wp

目录 前言 真亦假&#xff0c;假亦真 CSDN_To_PDF V1.2 前言 感谢17&#x1f474;没让我爆零 真亦假&#xff0c;假亦真 直接getshell不行&#xff0c;那就一波信息搜集呗&#xff0c;先开dirsearch扫一下 扫的过程中先试试常规的robots.txt,www.zip,shell.phps,.git,.sv…

使用Docker Compose一键部署前后端分离项目(图文保姆级教程)

一、安装Docker和docker Compose 1.Docker安装 //下载containerd.io包 yum install https://download.docker.com/linux/fedora/30/x86_64/stable/Packages/containerd.io-1.2.6-3.3.fc30.x86_64.rpm //安装依赖项 yum install -y yum-utils device-mapper-persistent-data l…

求整数各个数位上的数字之和 C语言

对于任意输入的整数&#xff0c;计算其各个数位上的数字之和。 输入格式 输入一个正整数 N。 输出格式 输出 N 的各个位上的数字之和。 数据范围 1 < N < 2^31 #include <stdio.h> #include <stdlib.h> int shu(int x){ int sum0; while(x>0)…

百度云加速方法「Cheat Engine」

加速网盘下载 相信经常玩游戏的小伙伴都知道「Cheat Engine」这款游戏内存修改器&#xff0c;它除了能对游戏进行内存扫描、调试、反汇编 之外&#xff0c;还能像变速齿轮那样进行本地加速。 这款专注游戏的修改器&#xff0c;被大神发现竟然还能加速百度网盘资源下载&#xf…

c++前言

目录 1. 什么是 C 2. C 发展史 3. C 的重要性 4. 如何学习 C 5. 关于本门课程 1. 什么是C C语言是结构化和模块化的语言&#xff0c;适合处理较小规模的程序。对于复杂的问题&#xff0c;规模较大的 程序&#xff0c;需要高度的抽象和建模时&#xff0c; C 语言则不合适…

【JavaParser笔记01】JavaParser解析Java源代码中的类信息(javadoc注释、类​​​​​​​名称)

这篇文章,主要介绍如何使用JavaParser解析Java源代码中的类信息(javadoc注释、类名称)。 目录 一、JavaParser依赖库 1.1、引入依赖 1.2、获取类注释信息

【Python进阶(一)】——异常与错误

&#x1f349;CSDN小墨&晓末:https://blog.csdn.net/jd1813346972 个人介绍: 研一&#xff5c;统计学&#xff5c;干货分享          擅长Python、Matlab、R等主流编程软件          累计十余项国家级比赛奖项&#xff0c;参与研究经费10w、40w级横向 文…

python | 输出n位数的各位位数

xinput(请输入一位n位数:)print(*map(int,x)) 在Python中&#xff0c;input() 函数接收用户的输入并返回一个字符串。当你执行 x input(请输入一位n位数:) 时&#xff0c;x 将包含用户输入的字符序列。 接下来&#xff0c;map(int, x) 会对字符串 x 中的每一个字符应用 int 函…

【uC/OS-III篇】uC/OS-III 移植到 STM32 简明教程

uC/OS-III 移植到 STM32 简明教程 一、uC/OS-III 介绍 二、获取UCOS-III源码 三、建立项目工程 四、解决工程编译报错 五、修改项目文件 下一篇博客&#xff1a; 【uC/OS-III篇】uC/OS-III 创建第一个任务&#xff08;For STM32&#xff09; 移植后的工程自取方式&#xf…

玩转Django分页器

一、Pagination 分页器编程步骤 View, 导入django.core.paginator.Paginator类&#xff0c;创建Paginator 对象时&#xff0c;输入qs对象&#xff0c;以及每页显示条数。 接收 URL, 从请求参数中读取page数值 &#xff0c;通过 paginator.page(page_num) 返回请求页的page_obj…

从 PG 技术峰会南京站汲取的那些干货

3月30日下午&#xff0c;“PostgreSQL 数据库技术峰会”南京站圆满举办。 这场峰会同步在线上直播&#xff0c;近 2000 人在线观看。 感谢各位大佬带来精彩演讲。 1. 可观测性 第一场正式演讲是白鳝前辈带来的《PostgreSQL 数据库的可观测性能力》。 数据库的可观测性的重要性不…

Mysql数据库:MHA高可用架构

目录 前言 一、MHA概述 1、什么是MHA 2、MHA的特点 3、MHA的组成 4、MHA的工作原理 5、故障切换备选主库的算法 二、部署MHA高可用架构 1、环境部署 2、部署主从同步 2.1 修改主配置文件并创建软链接 2.1.1 master 修改主配置文件并创建软连接 2.1.2 slave1 修改主…

os模块篇(四)

专栏目录 文章目录 专栏目录os.setregid(rgid, egid, /)os.setreuid(ruid, euid, /)os.getsid(pid, /)os.setsid()os.setuid(uid, /)os.strerror(code, /)os.umask(mask, /)os.uname()os.unsetenv(key, /)os.unshare(flags) os.setregid(rgid, egid, /) os.setregid(rgid, egi…

算法学习——LeetCode力扣补充篇5 (52. N 皇后 II、649. Dota2 参议院、1221. 分割平衡字符串、5. 最长回文子串)

算法学习——LeetCode力扣补充篇5 52. N 皇后 II 52. N 皇后 II - 力扣&#xff08;LeetCode&#xff09; 描述 n 皇后问题 研究的是如何将 n 个皇后放置在 n n 的棋盘上&#xff0c;并且使皇后彼此之间不能相互攻击。 给你一个整数 n &#xff0c;返回 n 皇后问题 不同的…

免费!微软推出全球第一个生成式AI认证

微软在官网宣布一项新的人工智能计划&#xff0c;包括与 LinkedIn联合推出免费的生成式AI课程和专业认证证书&#xff1b;微软与数据平台data.org、开源社区GitHub 等合作&#xff0c;发一起全球生成式AI挑战赛&#xff0c;为参赛的组织提供资金、技术、培训等帮助。 用户学习…

视觉SLAM理论与实践的学习链接汇总

仅供学习&#xff0c;在此感谢所有乐于分享知识的大佬们~ 一、 ORB_SLAM理论 视觉SLAM 前端 后端 回环 建图 1、 前端视觉里程计 1.1 特征点法 一文带你搞懂相机内参外参(Intrinsics & Extrinsics)-知乎 VSLAM 笔记——我们如何通过图像来计算位姿的变化&#xff…

文生图大模型三部曲:DDPM、LDM、SD 详细讲解!

1、引言 跨模态大模型是指能够在不同感官模态(如视觉、语言、音频等)之间进行信息转换的大规模语言模型。当前图文跨模态大模型主要有&#xff1a; 文生图大模型&#xff1a;如 Stable Diffusion系列、DALL-E系列、Imagen等 图文匹配大模型&#xff1a;如CLIP、Chinese CLIP、…

力扣刷题Days28-第二题-11.盛水最多的容器(js)

目录 1&#xff0c;题目 2&#xff0c;代码 3&#xff0c;学习与总结 3.1思路回顾 1&#xff0c;如何遍历 2&#xff0c;算法流程 3.2剖析问题 1&#xff0c;题目 给定一个长度为 n 的整数数组 height 。有 n 条垂线&#xff0c;第 i 条线的两个端点是 (i, 0) 和 (i, h…

Keil界面乱了,某些图标消失

文章目录 如图 如图 我都不知道怎么搞的第一个 重启界面解决了