【动态规划】【广度优先搜索】【状态压缩】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++**实现。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

03.时间轮

时间轮 1.为什么需要时间轮 海量的定时任务下&#xff0c;小顶堆时间复杂度比较高&#xff0c;性能差 2.时间轮是什么 时间轮这个技术其实出来很久了&#xff0c;在kafka、zookeeper、Netty、Dubbo等高性能组件中都有时间轮使用的方式 时间轮&#xff0c;从图片上来看&…

自定义数据集 - Dataset

文章目录 1. PASCAL VOC格式 划分训练集和验证集2. 自定义dataset 1. PASCAL VOC格式 划分训练集和验证集 import os import randomdef main():random.seed(0) # 设置随机种子&#xff0c;保证随机结果可复现files_path "./VOCdevkit/VOC2012/Annotations" # 指定…

Sentinel限流规则支持流控效果

流控效果是指请求达到流控阈值时应该采取的措施&#xff0c;包括三种&#xff1a; 1.快速失败&#xff1a;达到阈值后&#xff0c;新的请求会被立即拒绝并抛出FlowException异常。是默认的处理方式。 2.warm up&#xff1a;预热模式&#xff0c;对超出阈值的请求同样是拒绝并抛…

网络安全的信息收集方法有哪些?

网络安全攻击中的信息收集是攻击者为了了解目标系统的弱点、配置、环境和潜在的防御措施而进行的活动。以下是一些常见的信息收集手段&#xff1a; 开放网络资源查询&#xff1a; 使用搜索引擎查找关于目标组织的信息&#xff0c;包括新闻稿、社交媒体帖子、官方网站等。通过W…

答案之书程序

答案之书程序 需求&#xff1a;用户输入手机号码后4位或者生日&#xff0c;自动生成答案之书对应答案 效果图 C#代码实现过程 using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq;…

新买电脑配置不低却卡顿?

目录 前言&#xff1a; 电脑卡顿的原因 Windows 10必做的系统优化 禁用 IP Helper 关闭系统通知 机械硬盘开启优化驱动器功能 开启存储感知 前言&#xff1a; 新买的电脑配置不低&#xff0c;但却卡顿甚至程序不反应&#xff0c;这是怎么回事儿&#xff1f; 其实并不…

获取主流电商平台商品价格,库存信息,数据分析,SKU详情

要接入API接口以采集电商平台上的商品数据&#xff0c;可以按照以下步骤进行&#xff1a; 1、找到可用的API接口&#xff1a;首先&#xff0c;需要找到支持查询商品信息的API接口。这些信息通常可以在电商平台的官方文档或开发者门户网站上找到。 2、注册并获取API密钥&#x…

【代码随想录算法训练营第二十四天|回溯算法的理论基础、77. 组合】

代码随想录算法训练营第二十四天|回溯算法的理论基础、77. 组合 回溯算法的理论基础77. 组合 回溯算法的理论基础 这里我觉得《代码随想录》和y总的课都比较好了 《代码随想录》 &#xff1a; https://programmercarl.com/0077.%E7%BB%84%E5%90%88%E4%BC%98%E5%8C%96.html#%E5…

成人高考和自考到底应该选哪个呢?

在成人学历提升的各项方式之中 成人高考与自学考试经常会被人拿来对比 但它们之间的差别在哪里 又分别去适合什么类型的考生 成考自考报名一般8月底开始&#xff0c;要准备考试的考生需要提前做好准备了哦 成考自考报名都需要上传证件照&#xff0c;而且都很严格 大家可使用小程…

react 页签(自行封装)

思路&#xff1a;封装一个页签组件&#xff0c;包裹页面组件&#xff0c;页面渲染之后把数据缓存到全局状态实现页面缓存。 浏览本博客之前先看一下我的博客实现的功能是否满足需求&#xff0c;实现功能&#xff1a; - 页面缓存 - 关闭当前页 - 鼠标右键>关闭当前 - 鼠标右…

启发式教学是什么

学生们在上课时看似认真听讲&#xff0c;但是在下课后却一片茫然&#xff0c;不知道你讲了什么内容&#xff1f;这是因为你可能使用了传统的教学方法&#xff0c;而不是启发式教学。 启发式教学是指老师在教育教学中&#xff0c;采用引导、启示、激发等手段&#xff0c;调动学…

主板电路学习; 华硕ASUS K43SD笔记本安装win7X64(ventoy)

记录 老爷机 白色 华硕 K43SD 笔记本 安装 win7X64 1. MBR样式常规安装win7X64Sp1 (华硕 K43SD 安装 win7X64 ) 老爷机 白色 华硕 K43SD 笔记本 安装 win7X64 &#xff08;常规安装&#xff09; 设置&#xff1a; 禁用UEFI 启用AHCI ventoy制作MBR&#xff08;非UEFI&#…

安全防御-基础认知

目录 安全风险能见度不足&#xff1a; 常见的网络安全术语 &#xff1a; 常见安全风险 网络的基本攻击模式&#xff1a; 病毒分类&#xff1a; 病毒的特征&#xff1a; 常见病毒&#xff1a; 信息安全的五要素&#xff1a; 信息安全的五要素案例 网络空间&#xff1a…

Anything本地知识库问答系统:基于检索增强生成式应用(RAG)两阶段检索、支持海量数据、跨语种问答

QAnything本地知识库问答系统&#xff1a;基于检索增强生成式应用&#xff08;RAG&#xff09;两阶段检索、支持海量数据、跨语种问答 QAnything (Question and Answer based on Anything) 是致力于支持任意格式文件或数据库的本地知识库问答系统&#xff0c;可断网安装使用。…

Vue diff原理

✨ 专栏介绍 在当今Web开发领域中&#xff0c;构建交互性强、可复用且易于维护的用户界面是至关重要的。而Vue.js作为一款现代化且流行的JavaScript框架&#xff0c;正是为了满足这些需求而诞生。它采用了MVVM架构模式&#xff0c;并通过数据驱动和组件化的方式&#xff0c;使…

DAY15--learning English

一、积累 1.loyalty Bro had loyalty on that. 老兄对那个东西情有独钟。 2. consent its illegal to film anyone without consent in many country 。 在一些国家里面&#xff0c;没有经过别人的同意就去拍摄别人是违法的。 3. butt 4.disciplinary Welcome to our discip…

Django(九)

1. 用户登录-Cookie和Session 什么是cookie和session&#xff1f; 发送HTTP请求或者HTTPS请求(无状态&短连接) http://127.0.0.1:8000/admin/list/ https://127.0.0.1:8000/admin/list/http无状态短连接&#xff1a;一次请求响应之后断开连接&#xff0c;再发请求重新连…

CentOS 系统创建网卡bond0

很多时候在机房运维的过程中&#xff0c;我们会遇到客户要求的建立网卡光口的bond0设置&#xff0c;通俗点说就是将两个光口合并为一个口进行链接设置。创建这个设置是有两种设置&#xff0c;一是在安装系统的过程中对bond0进行创建设置&#xff0c;另一种就是通过系统里面对网…

C波段数据链和DAA技术实现BVLOS超视距飞行-乔克托族BEYOND计划获得FAA扩大批准

俄克拉荷马州的乔克托族(CNO)超越计划宣布&#xff0c;已从联邦航空管理局(FAA)获得了扩大的超视距(BVLOS)操作豁免的批准。这扩展了原有的豁免范围&#xff0c;覆盖约43英里长的区域&#xff0c;包括CNO医疗诊所、CNO新兴航空技术中心测试场以及其他设施。这是目前美国境内同类…

文件上传笔记整理

文件上传 web渗透的核心&#xff0c;内网渗透的基础 通过上传webshell文件到对方的服务器来获得对方服务器的控制权 成功条件 文件成功上传到对方的服务器&#xff08;躲过杀软&#xff09; 知道文件上传的具体路径 上传的文件可以执行成功 文件上传的流程 前端JS对上传文件进行…