最短路径算法Dijstra及优化
1.算法简介
迪杰斯特拉算法(Dijkstra)是由荷兰计算机科学家狄克斯特拉于1959年提出的,因此又叫狄克斯特拉算法。是从一个顶点到其余各顶点的最短路径算法,解决的是有权图中最短路径问题。迪杰斯特拉算法主要特点是从起始点开始,采用贪心算法的策略,每次遍历到始点距离最近且未访问过的顶点的邻接节点,直到扩展到终点为止。
2.算法流程
迪杰斯塔拉的思想比较容易理解,是采用的纯粹的贪心,但一般我们不需要证明贪心。有兴趣的可以看看这篇文章,里面提到了贪心,这里不再细说。这里我们采用邻接矩阵进行图存储。
注:图片来源于百度
假设我们需要求解A-F的最短路径,我们首先需要准备三个数组存储信息,这也是迪杰斯特拉的算法常用的三大数组。visited数组(防止一个点多次作为中间结点)、dist数组(存储距离数组,存储源点到其他点的目前已知最短距离),parents数组(保存当前结点路径的前一个结点,便于后续推导最短路径)。
ok,三大件准备完了,接下来就是初始化了,visited数组全部初始化为False,dist数组全部初始化为inf,parents数组全部初始化为-1。
接下来我们着重说一下,迪杰斯塔拉算法的整个流程。
1.首先,我们以起点A为中间点,遍历结点A-F,如果存在非0路径,我们可以更新dist数组,具体来说,可以得到如下。
A | B | C | D | E | F |
---|---|---|---|---|---|
inf | 1 | 2 | inf | inf | inf |
同时我们更新parents数组
A | B | C | D | E | F |
---|---|---|---|---|---|
-1 | A | A | -1 | -1 | -1 |
最后我们将A点利用visited数组标记为已访问过。
2.随后开始遍历其他结点作为中间结点,一共需要遍历n-1次,每次遍历时,取出dist中最小值所对应的结点,并继续将其作为中间结点,更新三大数组,具体的更新细则简单来说就是,短就更新。
最终我们将会得到起点A到其他所有点的最短路径。也当然得到了起点A到目标点的最短距离。
3.堆优化
我们知道,迪杰斯塔拉主要有两层循环,其中一层循环为,遍历n-1个结点作为中间结点,还有一层为遍历n个结点寻找目前dist数组下的最短的距离且未被访问过的距离。时间复杂度为O(n2)。
堆优化是什么?堆是什么,堆主要分为大根堆和小根堆,堆能够通过在O(logn)的复杂度下调整结构,使得堆结构能够将最大值或者最小值放在最上方,因而能够在O(1)的结构下查询最大值或最小值,如何进行堆优化?
我们可以将dist数组舍去,改为一个优先队列(小根堆),在每次有新的路径长度更新时,也把该路径的长度和结点放入优先队列中,于是我们就能更快的取出dist中最小值所对应的结点,因而我们可以将优先队列中存储元素结构设为:
struct node{int v;//结点int w;//路径长度
};
此外,除了堆优化外,dijstra算法还有一些优化如链式向前星优化等等,如果有时间我后面就补充。
4.实例
紧急救援
作为一个城市的应急救援队伍的负责人,你有一张特殊的全国地图。在地图上显示有多个分散的城市和一些连接城市的快速道路。每个城市的救援队数量和每一条连接两个城市的快速道路长度都标在地图上。当其他城市有紧急求助电话给你的时候,你的任务是带领你的救援队尽快赶往事发地,同时,一路上召集尽可能多的救援队。
输入格式:
输入第一行给出4个正整数N、M、S、D,其中N(2≤N≤500)是城市的个数,顺便假设城市的编号为0 ~ (N−1);M是快速道路的条数;S是出发地的城市编号;D是目的地的城市编号。
第二行给出N个正整数,其中第i个数是第i个城市的救援队的数目,数字间以空格分隔。随后的M行中,每行给出一条快速道路的信息,分别是:城市1、城市2、快速道路的长度,中间用空格分开,数字均为整数且不超过500。输入保证救援可行且最优解唯一。
输出格式:
第一行输出最短路径的条数和能够召集的最多的救援队数量。第二行输出从S到D的路径中经过的城市编号。数字间以空格分隔,输出结尾不能有多余空格。
输入样例:
4 5 0 3
20 30 40 10
0 1 1
1 3 2
0 3 3
0 2 2
2 3 2
输出样例:
2 60
0 1 3
具体来说,该题就是一个典型的dijstra的优化题,题目要求最短距离和最多救援队,因此我们需要增加两个数组来存储这两个信息,分别设为nums和cnt数组。代表含义分别为到达第i个结点的最短路径数目和到达第i个结点所能召集的最大救援队数。
此外,如何初始化两个数组,在初始化为0后,以起点为中间结点时,对于起点我们有cnt[s] = 起点点的消防队数量,对所有非0路径,我们可以得到num[i] = 1,cnt[i] = 起点的消防队数量+第i点的消防队数量。
代码如下:
cnt[s] = save_num[s]
for j in range(n):if edges[s][j] != 0:pri_queue.put((edges[s][j],j))parents[j] = sdist[j] = edges[s][j]cnt[j] = cnt[s] + save_num[j]nums[j] = 1
visited[s] = True
此外,如何在后续的不断更新,我们可以很容易知道。其中k为当前的中间结点。
1.如果更新到一个新的路径比原来的路径短,那么我们有num[j] = num[k],cnt[j] = cnt[k] + 第j点的消防队数量。
2.如果更新到一个新的路径和原来的路径一样长,那么我们有num[j] += num[k],cnt[j] = max(cnt[k] + 第j点的消防队数量,max[j]),因为在此之前就已经有更新过cnt[j]了,我们只需要保存最大的那个,因为题上要求最大召集数。
因此,我们可以很容易写出代码如下:
python
from math import inf
from queue import PriorityQueuen,m,s,d = map(int,input().split())
save_num = list(map(int,input().split()))
edges = [[0]*n for _ in range(n)]
for _ in range(m):x,y,c = map(int,input().split())edges[x][y] = cedges[y][x] = c#接下来开始迪杰斯塔拉
visited = [False] * n
parents = [-1] * n
nums = [0] * n#记录虽短路径数量
cnt = [0] * n#记录救援队数量
dist = [inf] * n
dist[s] = 0
pri_queue = PriorityQueue()#创建优先队列#先初始化
cnt[s] = save_num[s]
for j in range(n):if edges[s][j] != 0:pri_queue.put((edges[s][j],j))parents[j] = sdist[j] = edges[s][j]cnt[j] = cnt[s] + save_num[j]nums[j] = 1
visited[s] = True#然后开始遍历
for _ in range(1,n):#首先取出最短路径_,k = pri_queue.get()visited[k] = True#随后开始更新for j in range(n):if not visited[j]:if edges[k][j] != 0 and (dist[k] + edges[k][j] < dist[j]):dist[j] = dist[k] + edges[k][j]parents[j] = knums[j] = nums[k]cnt[j] = cnt[k] + save_num[j]pri_queue.put((dist[j],j))elif edges[k][j] != 0 and (dist[k] + edges[k][j] == dist[j]):#如果有相同路径长度nums[j] += nums[k]if cnt[j] < cnt[k] + save_num[j]:cnt[j] = cnt[k] + save_num[j]parents[j] = kprint(nums[d],cnt[d])
path = []
while d != -1:path.append(d)d = parents[d]
print(" ".join(map(str,reversed(path))))
不过很遗憾,对于这道题,我暂时还未找到能够利用python求解的方法,始终有一个会超时。c++却可以过,甚至不需要堆优化=-=。
c++
代码来源
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;
const int maxn = 505;int G[maxn][maxn]; // 边权重
int V[maxn]; // 顶点权重
int dist[maxn], parents[maxn];
int num[maxn]; // 记录最短路条数
int ans[maxn]; // 记录最优顶点权重
bool vis[maxn];struct node{int v;int w;
};struct cmp{bool operator () (const node &a, const node &b){return a.w > b.w;}
};void Dijkstra(int S, int D, int N){memset(dist, 0x3f, sizeof(dist));memset(parents, -1, sizeof(parents));dist[S] = 0;ans[S] = V[S];num[S] = 1;vis[S] = true;priority_queue<node, vector<node>, cmp> q;for(int i = 0; i < N; i++){if(!vis[i] && G[S][i]){q.push({i, G[S][i]});dist[i] = G[S][i];parents[i] = S;num[i] = num[S];ans[i] = ans[S] + V[i];}}while(!q.empty()){int v = q.top().v;int w = q.top().w;q.pop();if(vis[v]) continue;vis[v] = true;for(int i = 0; i < N; i++){if(!vis[i] && G[v][i]){if(dist[v] + G[v][i] < dist[i]){dist[i] = dist[v] + G[v][i];q.push({i, dist[i]});num[i] = num[v]; // 单路径时, 接上一个ans[i] = ans[v] + V[i];parents[i] = v;} else if(dist[v] + G[v][i] == dist[i]){num[i] += num[v]; // 重复路径时, 增加 // ans[i] = max(ans[i], ans[v] + V[i]);if(ans[i] < ans[v] + V[i]){ans[i] = ans[v] + V[i];parents[i] = v;}}}}if(v == D) return;}
}void Print(int D, int S){ // 输出最优解 if(parents[D] == -1 && D == S){cout << D;return;}Print(parents[D], S);cout << " " << D;
}int main() {int N, M, S, D;cin >> N >> M >> S >> D;for(int i = 0; i < N; i++) cin >> V[i];int v1, v2, w;for(int i = 0; i < M; i++){cin >> v1 >> v2 >> w;G[v1][v2] = G[v2][v1] = w;}Dijkstra(S, D, N);cout << num[D] << " " << ans[D] << endl;Print(D, S);return 0;
};cin >> N >> M >> S >> D;for(int i = 0; i < N; i++) cin >> V[i];int v1, v2, w;for(int i = 0; i < M; i++){cin >> v1 >> v2 >> w;G[v1][v2] = G[v2][v1] = w;}Dijkstra(S, D, N);cout << num[D] << " " << ans[D] << endl;Print(D, S);return 0;
}
最短路径算法Dijstra及优化相关推荐
- 详解五种最短路径算法及其区别(c++)
目录 一.朴素Dijkstra算法 二.堆优化的Dijkstra 三.bellman_ford算法 四.spfa算法 五.floyd算法 使用区别: 所有边权都是正数的单源最短路:朴素Dijkstra ...
- 最短路径算法---狄杰斯特拉算法
最短路径算法-狄杰斯特拉算法 一.介绍 这是一种按照路径长度递增的次序产生最短路径的算法,采用的是贪心的思想,对带权图(有向和无向均可)寻找最短路径;该算法对于不含负权的网来说,是目前已知的最快的单源 ...
- 最短路径算法——Dijkstra,Bellman-Ford,Floyd-Warshall,Johnson,无一幸免
文章出自:http://dsqiu.iteye.com/blog/1689163 最短路径算法--Dijkstra,Bellman-Ford,Floyd-Warshall,Johnson,无一幸免 本 ...
- (最短路径算法整理)dijkstra、floyd、bellman-ford、spfa算法
一.floyd 1.介绍 floyd算法只有五行代码,代码简单,三个for循环就可以解决问题,所以它的时间复杂度为O(n^3),可以求多源最短路问题. 2.思想: Floyd算法的基本思想如下:从任意 ...
- 几大最短路径算法比较
用于解决最短路径问题的算法被称做"最短路径算法",有时被简称作"路径算法".最常用的路径算法有: Dijkstra算法.A*算法.SPFA算法.Bellman- ...
- 棋盘最短路径 python_Dijkstra 最短路径算法 Python 实现
Dijkstra 最短路径算法 Python 实现 问题描述 使用 Dijkstra 算法求图中的任意顶点到其它顶点的最短路径(求出需要经过那些点以及最短距离). 以下图为例: 算法思想 可以使用二维 ...
- 沃舍尔算法_[数据结构拾遗]图的最短路径算法
前言 本专题旨在快速了解常见的数据结构和算法. 在需要使用到相应算法时,能够帮助你回忆出常用的实现方案并且知晓其优缺点和适用环境.并不涉及十分具体的实现细节描述. 图的最短路径算法 最短路径问题是图论 ...
- 数据结构与算法--图论最短路径算法应用-词阶求解
最短路径案例 词梯应用,在一个词梯中,每个单词均由前一个单词改变一个字母而得到.例如,我们通过一系列单字母替换而得到zero转换为five,如下:five:zero,hero,here,hire,fi ...
- 几个最短路径算法Floyd、Dijkstra、Bellman-Ford、SPFA的比较
几大最短路径算法比较 转自:http://blog.csdn.net/v_july_v/article/details/6181485 几个最短路径算法的比较: Floyd 求多 ...
最新文章
- 中科院陆汝钤获吴文俊人工智能最高成就奖,百度王海峰获吴文俊人工智能杰出贡献奖...
- SQL_Server_2005_日期和时间函数(描述及实例)
- Firetruck(DFS+回溯)
- 什么是 Silverlight?
- android应用APP中的页面响应时间测试
- OPA start up and wait for
- python文件操作总结
- 数据结构 --- 堆
- Qt学习笔记-Qt程序切换皮肤
- 详细介绍mysql-bin.000001文件的来源及处理方法
- 魅族16s Pro跑分曝光:高通骁龙855 Plus+UFS 3.0闪存
- HMM条件下的 前向算法 和 维特比解码
- 如何在linux上压缩文件夹,如何在Linux中使用命令压缩文件和文件夹
- VirtualBox中的虚拟网络环境设置
- WAP,手机网站建站资料收集 - 老古董
- 反双曲余弦函数之C#设计笔记(五)
- 利用PL/SQL查询:员工工资的等级
- (转)软件版本中的Alpha,Beta,RC,Trial是什么意思?
- Bootstrap抽样和Monte Carlo思想
- apmserv mysql5.1启动失败_apmserv启动失败的原因以及解决方法