【动态规划】【广度优先搜索】【状态压缩】847 访问所有节点的最短路径

作者推荐

视频算法专题

本文涉及知识点

动态规划汇总
广度优先搜索 状态压缩

LeetCode847 访问所有节点的最短路径

存在一个由 n 个节点组成的无向连通图,图中的节点按从 0 到 n - 1 编号。
给你一个数组 graph 表示这个图。其中,graph[i] 是一个列表,由所有与节点 i 直接相连的节点组成。
返回能够访问所有节点的最短路径的长度。你可以在任一节点开始和停止,也可以多次重访节点,并且可以重用边。
示例 1:
输入:graph = [[1,2,3],[0],[0],[0]]
输出:4
解释:一种可能的路径为 [1,0,2,0,3]
示例 2:
输入:graph = [[1],[0,2,4],[1,3,4],[2],[1,2]]
输出:4
解释:一种可能的路径为 [0,1,4,2,3]
参数范围
n == graph.length
1 <= n <= 12
0 <= graph[i].length < n
graph[i] 不包含 i
如果 graph[a] 包含 b ,那么 graph[b] 也包含 a
输入的图总是连通图

广度优先搜索

需要记录那些节点已经访问,用状态压缩 (1 << i )表示第i个节点已访问。
还要记录此路径的最后节点。
这两个状态相同,后面的路径则相同。 由于是广度优先搜索,所以路径短的先处理,每个状态只会处理一次。
vDis 记录各状态的最短路径数。
que 记录状态。
时间复杂度:O(n2nn) 枚举起点O(n) 枚举状态数O(2^n) 每个状态处理。

核心代码

class Solution {
public:int shortestPathLength(vector<vector<int>>& graph) {m_c = graph.size();m_iMaskCount = 1 << m_c;for (int i = 0; i < m_c; i++){BFS(graph, i);}return m_iRet;}void BFS(vector<vector<int>>& neiBo,int start){vector<vector<int>> vDis(m_c, vector<int>(m_iMaskCount, m_iNotMay));queue<pair<int, int>> que;auto Add = [&](int node, int iPreMask,int iNew){const int iMask = iPreMask | (1 << node);if (vDis[node][iMask] <= iNew ){return ;}vDis[node][iMask] = iNew;que.emplace(node, iMask);};Add( start,0, 0);while (que.size()){auto [preNode, preMask] = que.front();const int iNew = vDis[preNode][preMask]+1;que.pop();for (const auto& next : neiBo[preNode]){Add(next, preMask, iNew);}}for (const auto& v : vDis){m_iRet = min(m_iRet, v.back());}}const int m_iNotMay = 100'000;int m_c, m_iMaskCount;int m_iRet = m_iNotMay;
};

测试用例

template<class T>
void Assert(const T& t1, const T& t2)
{assert(t1 == t2);
}template<class T>
void Assert(const vector<T>& v1, const vector<T>& v2)
{if (v1.size() != v2.size()){assert(false);return;}for (int i = 0; i < v1.size(); i++){Assert(v1[i], v2[i]);}}int main()
{	vector<vector<int>> graph;{Solution sln;graph = { {1,2,3},{0},{0},{0} };auto res = sln.shortestPathLength(graph);Assert(res, 4);}{Solution sln;graph = { {1},{0,2,4},{1,3,4},{2},{1,2} };auto res = sln.shortestPathLength(graph);Assert(res, 4);}}

动态规划

节点的距离用多源路径的最短距离。

动态规划的状态表示

mask&(1 << next)表示经过了next节点。
vDis[node][mask] 有以下两种含义:
一, 以node结尾,经过mask指定节点的最短路径经过的节点数。
二,以node结尾,且只经过node节点一次,经过mask指定节点的最短路径经过的节点数。
含义二,如果存在,则是含义二,否则是含义一。 必须枚举所有符合含义二的可能。

动态规划的转移方程

vDis[next][maks|next]= MinSelf n e x t = 0 m c − 1 \Large_{next=0}^{m_c-1} next=0mc1vDis[i][mask]+距离(i,next)
vDis[i][mask] 必须合法,且mask不包括next节点

动态规划的填表顺序

mask从1到大,确保动态规划的无后效性。某路径的编码是mask,经过新节点next后,新编码为iNewMask。则iNewMask-mask = 1 << next
1 << next 恒大于0。

动态规划的初始值

全部为不存在的数

动态规划的返回值

Min j = 0 m c − 1 \Large_{j=0}^{m_c-1} j=0mc1vDis[j].back() -1

证明

将最短路径的重复节点删除,保留任意一个。删除后为: i 1 \Large_1 1 i 2 \Large_2 2 …i n \Large_n n 。任意i k \Large_k k到i k + 1 \Large_{k+1} k+1的路径一定是最短,否则替换成最短。直接枚举,12! 超时。 用动态规划,共2nn种状态,空间复杂度O(2nn),每种状态转移时间复杂度O(n),故总时间复杂度O(2nnn)。

代码

//多源码路径
template<class T, T INF = 1000 * 1000 * 1000>
class CFloyd
{
public:CFloyd(const  vector<vector<T>>& mat){m_vMat = mat;const int n = mat.size();for (int i = 0; i < n; i++){//通过i中转for (int i1 = 0; i1 < n; i1++){for (int i2 = 0; i2 < n; i2++){//此时:m_vMat[i1][i2] 表示通过[0,i)中转的最短距离m_vMat[i1][i2] = min(m_vMat[i1][i2], m_vMat[i1][i] + m_vMat[i][i2]);//m_vMat[i1][i2] 表示通过[0,i]中转的最短距离}}}};vector<vector<T>> m_vMat;
};class Solution {
public:int shortestPathLength(vector<vector<int>>& graph) {m_c = graph.size();m_iMaskCount = 1 << m_c;vector<vector<int>> mat(m_c, vector<int>(m_c, 1000 * 1000 * 1000));for (int i = 0; i < m_c; i++){for (const auto& j : graph[i]){mat[i][j] = 1;}}CFloyd floyd(mat);vector<vector<int>> vDis(m_c, vector<int>(m_iMaskCount, m_iNotMay));for (int i = 0; i < m_c; i++){	vDis[i][1 << i] = 1;}for (int mask = 1; mask < m_iMaskCount; mask++){for (int i = 0; i < m_c; i++){if (vDis[i][mask] >= m_iNotMay){continue;}for (int next = 0 ;next < m_c ;next++ ){if ((1 << next) & mask){continue;//已经访问}const int iNewMask = (1 << next) | mask;vDis[next][iNewMask] = min(vDis[next][iNewMask], vDis[i][mask] + floyd.m_vMat[i][next]);}}}int iRet = m_iNotMay;for (const auto& v : vDis){iRet = min(iRet, v.back());}return iRet-1;}const int m_iNotMay = 100'000;int m_c, m_iMaskCount;};

2023年1月

class Solution {
public:
int shortestPathLength(vector<vector>& graph) {
auto Add = [this](int iMask, int iPos, int iOpeNum)
{
if (INT_MAX != m_vMaskPosMinOpe[iMask][iPos])
{
return;
}
m_vQue.emplace_back(iMask, iPos);
m_vMaskPosMinOpe[iMask][iPos] = iOpeNum;
};
m_c = graph.size();
for (int i = 0; i < sizeof(m_vMaskPosMinOpe) / sizeof(m_vMaskPosMinOpe[0]); i++)
{
for (int j = 0; j < sizeof(m_vMaskPosMinOpe[0]) / sizeof(m_vMaskPosMinOpe[0][0]); j++)
{
m_vMaskPosMinOpe[i][j] = INT_MAX;
}
}
for (int i = 0; i < m_c; i++)
{
Add(1 << i, i, 0);
}
for (int i = 0; i < m_vQue.size(); i++)
{
const int iMask = m_vQue[i].first;
const int iPos = m_vQue[i].second;
for (auto& next : graph[iPos])
{
int iNewMask = iMask | (1 << next);
Add(iNewMask, next, m_vMaskPosMinOpe[iMask][iPos] + 1);
}
}
int iMin = INT_MAX;
for (int i = 0; i < sizeof(m_vMaskPosMinOpe[0]) / sizeof(m_vMaskPosMinOpe[0][0]); i++)
{
iMin = min(iMin, m_vMaskPosMinOpe[(1 << m_c) - 1][i]);
}
return iMin;
}
vector<std::pair<int,int>> m_vQue;
int m_vMaskPosMinOpe[1 << 12 ][12];
int m_c;
};

2023年8月

class Solution {
public:
int shortestPathLength(vector<vector>& graph) {
auto Add = [this](int iMask, int iPos, int iOpeNum)
{
if (INT_MAX != m_vMaskPosMinOpe[iMask][iPos])
{
return;
}
m_vQue.emplace_back(iMask, iPos);
m_vMaskPosMinOpe[iMask][iPos] = iOpeNum;
};
m_c = graph.size();
for (int i = 0; i < sizeof(m_vMaskPosMinOpe) / sizeof(m_vMaskPosMinOpe[0]); i++)
{
for (int j = 0; j < sizeof(m_vMaskPosMinOpe[0]) / sizeof(m_vMaskPosMinOpe[0][0]); j++)
{
m_vMaskPosMinOpe[i][j] = INT_MAX;
}
}
for (int i = 0; i < m_c; i++)
{
Add(1 << i, i, 0);
}
for (int i = 0; i < m_vQue.size(); i++)
{
const int iMask = m_vQue[i].first;
const int iPos = m_vQue[i].second;
for (auto& next : graph[iPos])
{
int iNewMask = iMask | (1 << next);
Add(iNewMask, next, m_vMaskPosMinOpe[iMask][iPos] + 1);
}
}
int iMin = INT_MAX;
for (int i = 0; i < sizeof(m_vMaskPosMinOpe[0]) / sizeof(m_vMaskPosMinOpe[0][0]); i++)
{
iMin = min(iMin, m_vMaskPosMinOpe[(1 << m_c) - 1][i]);
}
return iMin;
}
vector<std::pair<int,int>> m_vQue;
int m_vMaskPosMinOpe[1 << 12 ][12];
int m_c;
};

扩展阅读

视频课程

有效学习:明确的目标 及时的反馈 拉伸区(难度合适),可以先学简单的课程,请移步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/638883.shtml

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

相关文章

如何用“VMware安装Ubuntu”win11系统?

一、 下载Ubuntu 企业开源和 Linux |Ubuntu的 二、 安装 三、 启动虚拟机 选中Try or Install Ubuntu Server&#xff0c;按回车

数据结构与算法:图

文章目录 图1) 概念有向 vs 无向度权路径环图的连通性 2) 图的表示3) Java 表示4) DFS5) BFS6) 拓扑排序7) 最短路径DijkstraBellman-FordFloyd-Warshall 8) 最小生成树PrimKruskal 图 1) 概念 图是由顶点&#xff08;vertex&#xff09;和边&#xff08;edge&#xff09;组成…

Mysql学习笔记系列(一)

本次mysql系列不会讲解具体的查询语句&#xff0c;而是放在mysql的一些性能优化和一些特性上&#xff0c;是学习笔记&#xff0c;供大家参考补充。 慢查询 MySQL的慢查询&#xff0c;全名是慢查询日志&#xff0c;是MySQL提供的一种日志记录&#xff0c;用来记录在MySQL中响应…

Meta 标签的力量:如何利用它们提高网站的可见性(上)

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

如何在ubuntu22.04安装ROS2

ubuntu22.04安装ROS2 教程 选择对应版本进行安装设置编码添加源安装ROS2设置环境变量 运行ROS2 选择对应版本 通过官方网站&#xff0c;查询Ubuntu与ros对应的版本&#xff0c;版本不一致也会出现安装不成功。 https://wiki.ros.org/ROS/Installation 每一个都可以进行点击&a…

解决电脑文件大小写不敏感问题

第一步&#xff1a;以管理员的身份运行 CMD 第二步&#xff1a; 输入下面命令 fsutil file setCaseSensitiveInfo 路径 enable 路径改成目标文件夹的路径&#xff0c;比如说我也下面 Less-24 这个文件夹里面的文件全部都大小写敏感 这样就 OK 了&#xff0c;注意路径最后要加…

GitFlow工作流

基于 Git 这一版本控制系统&#xff0c;通过定义不同的分支&#xff0c;探索合适的工作流程来完成开发、测试、修改等方面的需求。 例如&#xff1a;在开发阶段&#xff0c;创建 feature 分支&#xff0c;完成需求后&#xff0c;将此分支合并到 develop 分支上&#xff1b;在发…

深度学习常用代码总结(k-means, NMS)

目录 一、k-means 算法 二、NMS 一、k-means 算法 k-means 是一种无监督聚类算法&#xff0c;常用的聚类算法还有 DBSCAN。k-means 由于其原理简单&#xff0c;可解释强&#xff0c;实现方便&#xff0c;收敛速度快&#xff0c;在数据挖掘、数据分析、异常检测、模式识别、金…

PHP+vue+Mysql家庭理财管理系统演5x6nf

本文着重阐述了收支管理系统的分析、设计与实现&#xff0c;首先介绍开发系统和环境配置、数据库的设计&#xff0c;对系统的功能需求作出分析&#xff0c;根据需求对系统进行设计&#xff0c;明确各个部分的规范&#xff0c;来完成系统的设计。最后在对设计的系统进行一系列的…

k8s1.27.2版本二进制高可用集群部署

文章目录 环境软件版本服务器系统初始化设置关于etcd签名证书etcd集群部署负载均衡器组件安装设置关于k8s自签证书自签CAkube-apiserver 自签证书kube-controller-manager自签证书kube-scheduler自签证书kube-proxy 自签证书admin 自签证书 控制平面节点组件部署**部署kube-api…

Spring 事务原理一

从本篇博客开始&#xff0c;我们将梳理Spring事务相关的知识点。在开始前&#xff0c;想先给自己定一个目标&#xff1a;通过此次梳理要完全理解事务的基本概念及Spring实现事务的基本原理。为实现这个目标我想按以下几个步骤进行&#xff1a; 讲解事务中的一些基本概念使用Sp…

x-cmd pkg | jq - 命令行 JSON 处理器

目录 简介首次用户功能特点类似工具进一步探索 简介 jq 是轻量级的 JSON 处理工具&#xff0c;由 Stephen Dolan 于 2012 年使用 C 语言开发。 它的功能极为强大&#xff0c;语法简洁&#xff0c;可以灵活高效地完成从 JSON 数据中提取特定字段、过滤和排序数据、执行复杂的转…

Java NIO (三)NIO Channel类

1 概述 前面提到&#xff0c;Java NIO中一个socket连接使用一个Channel来表示。从更广泛的层面来说&#xff0c;一个通道可以表示一个底层的文件描述符&#xff0c;例如硬件设备、文件、网络连接等。然而&#xff0c;远不止如此&#xff0c;Java NIO的通道可以更加细化。例如&a…

【GitHub项目推荐--GitHub 上的考研神器】【转载】

如果有打算考研的读者&#xff0c;这些开源项目不能错过。把各个学校近几年考研初试真题分享给大家&#xff08;包括 408&#xff09;&#xff0c;应该能帮上大家&#xff0c;文末有下载方式。 同时&#xff0c;我把盘点的开源相关的学习项目更新到 Awesome GiHub Repo&#xf…

【GitHub项目推荐--智能家居项目】【转载】

如果你具备硬件、软件知识&#xff0c;这个项目肯定符合你的胃口。 物美智能是一套软硬件结合的开源项目&#xff0c;该系统可助你快速搭建自己的智能家居系统。你可以学习到设备的集成和软硬件交互。 PC 端或者手机与服务端通信&#xff0c;单片机可以接受遥控设备和服务器的…

硬件-11-服务器的基础知识

参考服务器基础知识大科普 1 电视剧背景 服务器被誉为互联网之魂。 电视剧《创业年代》是一部有冯绍峰和袁姗姗等人联手主演的一部讲述我国第一批科技创业者创业故事的电视剧&#xff0c;可以说是他们铲下了建设中关村的第一捧土。 电视剧《创业年代》中的潮信公司并没有…

【神经网络】火箭点火发射-诠释一场数据与学习的奇妙之旅

火箭点火发射来理解神经网络的故事细节 在一个充满科技气息的研究室里&#xff0c;一群科学家们正在忙碌地准备着一次重要的火箭点火发射。这次发射不仅是一次航天探索的壮丽征程&#xff0c;更是一场利用神经网络处理数据的智慧之旅。 在火箭发射的背后&#xff0c;神经网络…

中仕教育:研究生毕业可以考选调生吗?

选调生的报考条件之一是应届生&#xff0c;研究生毕业也属于应届生&#xff0c;所以是可以报考的。 选调生不同学历的年龄限制&#xff1a; 1.应届本科生&#xff1a;年龄在25岁以内 2.应届研究生&#xff1a;年龄在30岁以内 3.应届博士生&#xff1a;年龄在35岁以内 研究…

excel统计分析——Tukey法多重比较

参考资料&#xff1a;生物统计学 https://real-statistics.com/one-way-analysis-of-variance-anova/unplanned-comparisons/tukey-hsd/ Tukey法是基于学生化极差分布计算最小显著极差&#xff08;LSR&#xff09;&#xff0c;根据平均数个数调整最小显著极差。 LSR&#xff1…

LINUX常用工具之sudo权限控制

一、Sudo基本介绍 sudo是Linux 中用于允许特定用户以超级用户或其他特权用户的身份执行特定的命令或任务。sudo 提供了一种安全的方法&#xff0c;使用户能够临时获取额外的权限&#xff0c;而不需要以完全超级用户的身份登录系统。sudo也可以用了设置黑名单命令清单&#xff…