首页 > 其他分享 >最短路径问题

最短路径问题

时间:2024-04-07 16:14:08浏览次数:13  
标签:v0 dist int 路径 最短 问题 path 顶点

1. 最短路径问题

如果从图中某一顶点(称为源点)到达另一顶点(称为终点)的路径可能不止一条,如何找到一条路径,使得沿此路径各边上的权值总和(即从源点到终点的距离)达到最小,这条路径称为最短路径(shortest path)

根据有向网或无向网中各边权值的取值情形及问题求解的需要,最短路径问题分为以下三种情形,分别用不同的算法求解: 

  • 求单源最短路径(边的权值非负) - Dijkstra 算法,所谓单源最短路径就是固定一个顶点为源点,求源点到其他每个顶点的最短路径;
  • 求单源最短路径(边的权值允许为负值, 但不存在负权值回路) - Bellman-Ford 算法
  • Bellman-Ford 算法的改进 - SPFA 算法
  • 求所有顶点之间的最短路径(边的权值允许为负值,但不存在负权值回路) - Floyd算法。 

2. Dijkstra 算法 

2.1 算法思想 

给定一个带权有向图(即有向网) G 和源点 v0,求 v0 到 G 中其他每个顶点的最短路径。限定各边上的权值大于或等于 0。 

例如,在图 4.1(a)中,假设源点为顶点 0,则源点到其他顶点的最短距离分别为:

  • 顶点 0 到顶点 1 的最短路径距离是: 20,其路径为 v0→v2→v1;
  • 顶点 0 到顶点 2 的最短路径距离是: 5,其路径为 v0→v2;
  • 顶点 0 到顶点 3 的最短路径距离是: 22,其路径为 v0→v2→v5→v3;
  • 顶点 0 到顶点 4 的最短路径距离是: 28,其路径为 v0→v2→v1→v4;
  • 顶点 0 到顶点 5 的最短路径距离是: 12,其路径为 v0→v2→v5。

这些最短路径距离及对应的最短路径是怎么求出来的? 

为求得这些最短路径, Dijkstra 提出按路径长度的递增次序,逐步产生最短路径的算法。首先求出长度最短的一条最短路径,再参照它求出长度次短的一条最短路径,依次类推,直到从源点v0 到其它各顶点的最短路径全部求出为止。 

在 Dijkstra 算法里, 为了求源点 v0 到其他各顶点 vi 的最短路径及其长度, 需要设置 3 个数组: 

  • dist[n]dist[i]表示当前找到的从源点 v0 到终点 vi 的最短路径的长度,初始时, dist[i]为 Edge[v0][i],即邻接矩阵的第 v0 行。 
  • S[n]S[i]为 0 表示顶点 vi 还未加入到集合 S 中, S[i]为 1 表示 vi 已经加入到集合 S 中。初始时, S[v0]为 1,其余为 0,表示最初集合 S 中只有顶点 v0。 
  • path[n]path[i]表示 v0 到 vi 的最短路径上顶点 vi 的前一个顶点序号。采用“倒向追踪”的方法,可以确定 v0 到顶点 vi 的最短路径上的每个顶点。 

在 Dijkstra 算法里,重复做以下 3 步工作:

  • 在数组 dist[n]里查找 S[i] != 1,并且 dist[i]最小的顶点 u;
  • 将 S[u]改为 1,表示顶点 u 已经加入进来了;
  • 修改 T 集合中每个顶点 vk 的 dist 及 path 数组元素值: 当 S[k] != 1, 且顶点 u 到顶点 vk有边(Edge[u][k]<MAX),且 dist[u] + Edge[u][k] < dist[k],则修改 dist[k]为 dist[u] + Edge[u][k],修改 path[k]为 u。 

利用 Dijkstra 算法求上图(a)中顶点 0 到其他各顶点的最短路径长度,并输出对应的最短路径 :

分析:

  • Dijkstra 算法的 3 个数组: S、 dist、 path 初始状态如下图(a)所示(源点为顶点 0):
  • S[0]为 1,其余为 0,表示初始时, S 集合中只有源点 v0,其他顶点都是在集合 T 中;
  • dist 数组各元素的初始值就是邻接矩阵的第 v0 行对应的元素值;
  • 在 path 数组中, path[2]和 path[3]为 0,表示当前求得的顶点 0 到顶点 2 的最短路径上,前一个顶点是顶点 0;顶点 0 到顶点 3 的最短路径上,前一个顶点也是 0;其余元素值均为-1,表示顶点 0 到其余顶点没有直接路径。 

在 Dijkstra 算法执行过程中,以上 3 个数组各元素值的变化如图(b)~(f)所示。以图(b)为例加以解释。首先在 dist 数组的各元素 dist[t]中,选择一个满足 S[t]为 0、且 dist[t]取最小值,求得的最小值为 5,用粗体、斜体标明,对应顶点 v2;然后将 S[2]修改成 1,用粗体、下划线标明,表示将顶点 v2 加入到集合 S 中;最后修改 dist 数组和 path 数组中某些元素的值: 

  • 修改条件是: S[k]为 0,顶点 v2 到顶点 vk 有直接路径,且 dist[2] + Edge[2][k] < dist[k]。 
  • 修改方法是:将 dist[k]修改成 dist[2] + Edge[2][k],将 path[k]修改成 2。所有修改过的值用粗体、下划线标明。 

当顶点 0 到其他各顶点的最短路径长度求解完毕后, 如何根据 path 数组求解顶点 0 到其他各顶点 vk 的最短路径?方法是:从 path[k]开始,采用“倒向追踪”方法,一直找到源点 v0。

  • 设 k 为 4, 则因为 path[4] = 1, 说明最短路径上顶点 4 的前一个顶点是顶点 1; path[1] = 2,说明顶点 1 的前一个顶点是顶点 2; path[2] = 0,说明顶点 2 的前一个顶点是顶点 0,这就是源点; 所以从源点 v0 到终点 v4 的最短路径为: v0→v2→v1→v4, 最短路径长度为 dist[4] = 28。 

2.2 算法代码

#include <iostream>


#define INF 10000
#define MAXL 21


using namespace std;

int n, m;

int Edge[MAXL][MAXL];

int S[MAXL];
int path[MAXL];
int dist[MAXL];


void Dijkstra(int v0) {
	for (int i = 0; i < n; i++) { // 初始化三个数组
		S[i] = 0;
		dist[i] = Edge[v0][i];
		if (i != v0 && dist[i] < INF) {
			path[i] = v0;
		}
		else {
			path[i] = -1;
		}
	}

	S[0] = 1;
	dist[v0] = 0;

	for (int i = 0; i < n - 1; i++) { // 从顶点 v0 确定 n-1 条最短路径
		int Min = INF;
		int u = v0;
		for (int j = 0; j < n; j++) { // 选择当前集合 T 中具有最短路径的顶点 u
			if (!S[j] && dist[j] < Min) {
				u = j;
				Min = dist[j];
			}
		}

		S[u] = 1; // 将顶点 u 加入到集合 S,表示它的最短路径已求得

		for (int k = 0; k < n; k++) { // 修改 T 集合中顶点的 dist 和 path 数组元素值
			if (!S[k] && Edge[u][k] < INF && Edge[u][k] + dist[u] < dist[k]) {
				dist[k] = Edge[u][k] + dist[u];
				path[k] = u;
			}
		}
	}
}

int main() {
	int u, v, w;

	memset(Edge, 0, sizeof(Edge));

	cin >> n >> m;

	for (int i = 0; i < m; i++) {
		cin >> u >> v >> w;
		Edge[u][v] = w; // 有向图
	}

	for (int i = 0; i < n; i++) { // 初始化Edge矩阵
		for (int j = 0; j < n; j++) {
			if (i == j) {
				Edge[i][j] = 0;
			}
			else if (Edge[i][j] == 0) {
				Edge[i][j] = INF;
			}
		}
	}

	Dijkstra(0);

	for (int i = 1; i < n; i++) {
		cout << dist[i] << endl;
	}

	return 0;
}

 

标签:v0,dist,int,路径,最短,问题,path,顶点
From: https://www.cnblogs.com/love-9/p/18119248

相关文章

  • 洛谷题单指南-数学基础问题-P1017 [NOIP2000 提高组] 进制转换
    原题链接:https://www.luogu.com.cn/problem/P1017题意解读:负进制数的转换。解题思路:下面给出两种思路1、枚举法从数据范围来看,∣n∣≤37336,因此,可以对该r进制的数进行枚举,每一次枚举,都计算r进制数对应的十进制数是否和n相等,相等则输出该r进制数。主要问题就是要解决r进制......
  • 【Java业务需求解决方案】分布式锁应用详情,多种方案选择,轻松解决,手把手操作(非全数
    目录背景:解决方案:分布式锁方案一(不建议,但原理得懂):Redis锁setnx与业务代码处理雏形代码产生问题一:锁释放问题代码改造:锁添加过期时间产生问题二:锁被别的线程误删代码改造:添加setnx锁请求标识防勿删产生问题三:递归容易造成内存溢出代码改造:递归改造while循环产生......
  • 关于layui的单图片上传遇坑-field-input-name问题解决
    直接上代码注意field:'ymftimage'layui.use(function(){varupload=layui.upload;varlayer=layui.layer;varelement=layui.element;var$=layui.$;//单图片上传varuploadInst=upload.render({elem:'#ID-upload-demo-btn',......
  • 单片机 Mooc 课程讨论区问题集锦
    单片机Mooc课程讨论区问题集锦单片机和嵌入式系统的根本区别和联系是什么?答:单片机和嵌入式系统的根本区别在于是否使用操作系统,没有采用OS的32位的ARM处理器就是32位单片机。在没有学过微机原理的情况下学习单片机要注意哪些问题?答:该课程就是给没有计算机基础的......
  • Stream流sorted的多级排序问题(巨坑)
    https://blog.csdn.net/qq_28165595/article/details/131152763 前言之前在给一个List集合中对象进行多属性排序,比如先根据对象中的A属性正序排序,若两个对象的A属性值相同,再根据对象的B属性进行正序排序。这种排序在实际开发中比较常见,但是这里面有个坑。举个例子先初始化一个......
  • 面试常问问题——常用linux命令及如何查看日志?
    一、常用linux命令pwd   查看当前目录位置lscpgrepcdmvtaillesstouchmkdirpsaux  查看系统所有进程数据kill-oPID  强制中断一个进程的进行chmod  -Rxyz 文件或目录   改变文件或......
  • 2024年最新,Linux平台 CentOS8安装mysql流程,以及可能遇到的问题
    0.删除mysql如果下载过mysql,请先删除mysql,不确定的也可以先查询一下查询命令: rpm-qa|grepmysqlrpm-qa|grepmariadb删除查询到的这些文件rpm-e--nodeps//查询到的软件名称例如:查询到的mysql相关文件删除mysql相关文件,并查询***别忘记去解压目录下......
  • 货币系统—背包问题—python题解
    题目链接:货币系统题目描述:给定V种货币(单位:元),每种货币使用的次数不限。不同种类的货币,面值可能是相同的。现在,要你用这V种货币凑出N元钱,请问共有多少种不同的凑法。输入格式第一行包含两个整数V和N。接下来的若干行,将一共输入V个整数,每个整数表示一种货币的......
  • 关于Axios的异域问题
    需要建一个类,在类中修改需要访问的前端端口: 代码如下:importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.web.cors.CorsConfiguration;importorg.springframework.web.cors.UrlB......
  • 两个字符串间的最短路径问题【华为OD机试】(JAVA&Python&C++&JS题解)
    一.题目-两个字符串间的最短路径问题给定两个字符串,分别为字符串A与字符串B。例如A字符串为ABCABBA,B字符串为CBABAC可以得到下图m*n的二维数组,定义原点为(0,0),终点为(m,n),水平与垂直的每一条边距离为1,映射成坐标系如下图。从原点(0,0)到(0,A)为水平边,距离为1,从(0,A)......