本篇文章接上(上)的经典例应用:
④一场说走就走的旅行——最短路径
当你想要旅行的地点有多个,想要计算最短路线,估算所需时间时,该如何计算?
【思考过程】这是一个求单源最短路径的问题。给定有向带权图G=(V,E),其中每条边的权是非负实数。此外,给定V中的一个顶点,成为源点。现在要计算从源到所有其他各顶点的最短路径长度,这里路径长度指路上各边的权之和。如何求源头点到其他各点的最短路径呢?
【算法设计】(1)数据结构。设置地图的带权邻接矩阵为map[] [] ,即如果从源点 u 到顶点 i 有边,就令 map[u] [i] 等于 <u,i> 的权值,否则 map[u] [i]=∞(无穷大);采用一维数组 dist[i] 来记录从源点到 i 顶点的最短路径长度;采用一维数组 p[i] 来记录最短路径上 i 顶点的前驱。
(2)初始化。令集合 S={u},对于集合V-S中的所有顶点 x ,初始化 dist[i]=map[u][i],如果源点 u 到顶点 i 有边相连,初始化 p[i]=u,否则 p[i]=-1。
(3)找最小。在集合 V-S 中依照贪心策略来寻找使得 dist[j]具有最小值的顶点 t ,即 dist[t]=min(dist[j] | j 属于V-S集合),则顶点 t 就是属于集合V-S中距离源点 u 最近的顶点。
(4)加入S战队。 将顶点 t 加入集合S中,同时更新V-S。
(5)判结束。 如果集合V-S为空,算法结束,否则转(6)。
(6)借东风。在(3)中已经找到了源点到 t 的最短路径,那么对集合V-S中所有与顶点 t 相邻的顶点 j ,都可以借助 t 走捷径。如果 dist[j] > dist[t] + map[t] [j],则 dist[j] = dist[t] + map[t] [j],记录顶点 j 的前驱为 t,有 p[j] = t,转(3)。
由此,可求得从源点 u 到图G的其余各个顶点的最短路径及长度,也可通过数组 p[]逆向找到最短路径上经过的城市。
【代码实现】
#include <cstdio>
#include<iostream>
#include<cstring>
#include<windows.h>
#include<stack>
using namespace std;
const int N = 100; //城市的个数可修改
const int INF = le7; //初始化无穷大为10000000
int map[N] [N],dist[N],p[N],n,m; //n为城市的个数,m为城市间路线的条数
bool flag[N]; //如果flag[i]等于ture,说明顶点i已经加入到集合S;否则顶点i属于集合V-S
void Dijkstra(int u){
for(int i=1;i<n;i++){
dist[i]=map[u][i]; //初始化源点u到其他各个顶点的最短路径长度
flag[i] = flase;
if(dist[i] == INF)
p[i]=-1; //源点u到该顶点的路径长度为无穷大,说明顶点i与源点u不相邻
else
p[i]=u; //说明顶点i与源点u相邻,设置顶点i的前驱p[i]=u
}
dist[u]=0;
flag[u]=ture; //初始时,集合S中只有一个元素:源点u
for(int i=1;i<n;i++){
int temp = INF,t=u;
for(int j=1;j<=n;j++){
//在集合V-S中寻找距离源点u最近的顶点t
if(!flag[j]&&dist[j]<temp){
t = j;
temp = dist[j];
}
if(t==u) return; //找不到t,跳出循环
flag[t]=ture; //否则,将t加入集合
for(int j=1;j<=n;j++){
//更新集合V-S中与t邻接的顶点与源点u的距离
if(!flag[j]&&map[t][j]<INF){
//!flag[j]表示j在V-S中
if(dist[j]>(dist[t]+map[t][j])){
dist[j]=dist[t]+map[t][j];
p[j]=t;