dijkstra算法 基础版+邻接表、优先队列优化 双版本
0 例题
Problem
在每年的校赛里,所有进入决赛的同学都会获得一件很漂亮的t-shirt。但是每当我们的工作人员把上百件的衣服从商店运回到赛场的时候,
却是非常累的!所以现在他们想要寻找最短的从商店到赛场的路线,你可以帮助他们吗?
Input
输入包括多组数据。每组数据第一行是两个整数N、M(N<=100,M<=10000),N表示成都的大街上有几个路口,标号为1的路口是商店所在地,标号为N的路口是赛场所在地,M则表示在成都有几条路。N=M=0表示输入结束。接下来M行,每行包括3个整数A,B,C(1<=A,B<=N,1<=C<=1000),表示在路口A与路口B之间有一条路,我们的工作人员需要C分钟的时间走过这条路。输入保证至少存在1条商店到赛场的路线。
Output
对于每组输入,输出两行行,第一行表示工作人员从商店走到赛场的最短时间,第二行表示路径各点。
Sample Input
2 1
1 2 3
3 3
1 2 5
2 3 5
3 1 2
0 0
Sample Output
3
1 2
2
1 3
1 例题分析
本题求得是源点1到点N的最短路径最少花费以及具体路径点。所以很容易地想到求单源最短路径求解算法——dijkstra算法。
在每条边权值为非负实数的有向带权图中,给定一个点称为源点,求该源点到其他点的最短路径长度就是单源最短路径问题。在带权图中,两个顶点的路径长度指它们之间的路径上各边的权值之和。
dijkstra算法是解决单源最短路径问题的贪心算法,它先求出长度最短的一条路径,然后参照该路径求出长度次短的一条路径,直到求出源点到其它各个顶点的最短路径。
dijkstra算法的基本思想是假定源点为u,顶点集合V被划分为两部分,集合S和集合V-S。初始时集合S中只有源点u,S中的顶点到远点的最短路径已确定,V-S中的顶点到源点的最短路径待定。从源点出发只经过S中的点到达V-S中的点的路径为特殊路径,用数组dist[]记录每个顶点所对应的最短特殊路径长度。
dijkstra算法采用的贪心策略是选择特殊路径长度最短的路径,将其连接的V-S中的顶点加入集合S,同时更新数组dist[]。一旦S包含了所有顶点,dist[]就是从源点到其他所有顶点的最短路径长度。
2 代码
2.1 邻接矩阵原始代码
#include <iostream>
#include <queue>
#define MAX 1<<30
#define MAXN 1105
using namespace std;
int map[MAXN][MAXN], dist[MAXN], flag[MAXN], p[MAXN], n, m;
/*map[][]为邻接矩阵,dist[]记录各点到源点的距离;
* flag[]记录顶点的最短路径是否已经找出;所有的点在V集合中,找出就放在S集合里,
* 否则就在V-S集合中,flag=1代表在S集合,否则在V-S集合中;
* p[]是记录最短路径上某一顶点的前驱顶点*/
void dij(int);
void findpath(int);
int main() {
while (cin >> n >> m, n && m) {
for (int i = 0; i < 1105; i++) {
for (int j = 0; j < 1105; j++) {
map[i][j] = MAX;
}
}
for (int i = 1; i <= m; i++) {
int a, b, c;
cin >> a >> b >> c;
map[a][b] = c;
map[b][a] = c;
}/*存储图*/
dij(1);
cout << dist[n] << endl;
findpath(1);
cout << endl;
}
return 0;
}
void dij(int v) {
for (int i = 1; i <= n; i++) {
dist[i] = map[v][i];
flag[i] = 0;
if (dist[i] == MAX) {
p[i] = -1;/*源点到该点的距离为无限大,说明源点与该点不相邻*/
} else {
p[i] = v;/*说明与源点v相邻*/
}
}
flag[v] = 1;/*初始化dist[]和flag[]*/
for (int i = 1; i <= n; i++) { /*找到每个点距离源点最短路径*/
int temp = MAX, t = v;
for (int j = 1; j <= n; j++) {
if (!flag[j] && dist[j] < temp) {/*找到V-S集合(flag==0代表属于该集合)中距离源点最小的点,即dist[]最小的点*/
t = j;
temp = dist[j];
}
}
if (t == v) {
return;
}
flag[t] = 1;/*将找到的最小dist[]的点t加入S集合*/
for (int j = 1; j <= n; j++) {/*将找到的点t加入S集合后,更新与t相邻的点的dist[]值*/
if (!flag[j] && map[t][j] < MAX) {
if (dist[j] > temp + map[t][j]) {
dist[j] = temp + map[t][j];
p[j] = t;
}
}
}
}
}
void findpath(int v) {
deque<int> path;
path.clear();
/*for (int i = 1; i <= n; i++) {
int x = n;
if (x == -1 && v != i) {
cout << "源点到第" << i << "点" << ",无路可达" << endl;
continue;
}
while (x != -1) {
path.push_front(x);
x = p[x];
}
cout << "源点到第" << i << "点的最短路径为:";
while (!path.empty()) {
cout << path.front();
if (path.size() != 1) {
cout << "--";
}
path.pop_front();
}
cout << endl;
}*/
/*由于此题只需要找出第N点的最短路径,所以不需要写出上面的代码,上面的代码是所有点的最短路径
* 本题的代码如下*/
int x = n;
if (x == -1 && v != n) {
cout << "no path" << endl;
}
while (x != -1) {
path.push_front(x);
x = p[x];
}
cout << "path:";
while (!path.empty()) {
cout << path.front();
if (path.size() != 1) {
cout << "--";
}
path.pop_front();
}
cout << endl;
}
2.2 邻接表+优先队列优化后的代码
dijkstra算法实际上就是BFS算法的“升级版”。BFS算法每次选择队列中的队首元素进行扩展,扩展出队首元素的相邻元素(并且未曾访问过),将这些元素加入队列;dijkstra算法是BFS的升级版。当一个图中的每条边都加上权值后,BFS就没办法求一个点到另一个点的最短路径了。这时候,需要用到dijkstra算法。从最基本原理上讲,把BFS改成dijkstra算法,只需要把“队列”改成“优先队列”就可以了。

浙公网安备 33010602011771号