关于求图的顶点间最短路径问题,基本分为两种算法:
- Dijkstra算法
- Floyd算法
Dijkstra算法是用来求图中某个源点到其他顶点的最短路径的,而Floyd是用来求图中任意两个顶点间的最短路径。原理上Floyd可以对Dijkstra算法遍历以便所有顶点得到,但是Flody的写法更简单一点。
下面以一道经典例题为例
题目来源:PTA
旅游规划
有了一张自驾旅游路线图,你会知道城市间的高速公路长度、以及该公路要收取的过路费。现在需要你写一个程序,帮助前来咨询的游客找一条出发地和目的地之间的最短路径。如果有若干条路径都是最短的,那么需要输出最便宜的一条路径。
输入格式:
输入说明:输入数据的第1行给出4个正整数N、M、S、D,其中N(2≤N≤500)是城市的个数,顺便假设城市的编号为0~(N−1);M是高速公路的条数;S是出发地的城市编号;D是目的地的城市编号。随后的M行中,每行给出一条高速公路的信息,分别是:城市1、城市2、高速公路长度、收费额,中间用空格分开,数字均为整数且不超过500。输入保证解的存在。
输出格式:
在一行里输出路径的长度和收费总额,数字间以空格分隔,输出结尾不能有多余空格。
输入样例:
4 5 0 3
0 1 1 20
1 3 2 30
0 3 4 10
0 2 2 20
2 3 1 20
输出样例:
3 40
附上利用Dijkstra算法的代码(分析可见代码注释)(AC源码)
#include <iostream>
#include <cstring>
using namespace std;
const int N = 510;
int dist[N][N];//两个城市间的距离
int fee[N][N]; //城市间高速收费
bool st[N]; //存储某个城市的最短路是否已经遍历过
int n,m,s,d; //n为城市数,m为路径数,s为起点,d为终点
void Dijstra(int s,int b) //s为起点城市,b为终点城市
{
dist[s][s] = 0; //初始化
for(int i = 0; i < n; i ++)//一共遍历n次
{
int t = -1;
for(int j = 0; j < n; j ++)
{
if(!st[j] && (t == -1 || dist[s][t] > dist[s][j]))
{
t = j;//调用一个没有调用过的中间顶点
}
}
st[t] = true;//某点被用过则标记以下
for(int j = 0; j < n; j ++)//遍历所有点
{
if(dist[s][j] > dist[s][t] + dist[t][j]) //源点s通过t到达j 比 s直接到j 更短
{
dist[s][j] = dist[s][t] + dist[t][j]; //更新s到j的最短路径
fee[s][j] = fee[s][t] + fee[t][j]; //更新收费额
}
else if(dist[s][j] == dist[s][t] + dist[t][j])
fee[s][j] = min(fee[s][j], fee[s][t] + fee[t][j]);//路径相等时取收费最小的
else continue;
}
}
cout << dist[s][b] << ' ' << fee[s][b];
}
int main()
{
cin >> n >> m >> s >> d;
memset(dist, 0x3f, sizeof dist);//初始化dist数组为inf
for(int i = 0; i < m; i ++)
{
int a,b,len,fe;
cin >> a >> b >> len >> fe;
if(dist[a][b] > len) //若两个城市间不止一条路径
{
dist[a][b] = dist[b][a] = len;//无向图~~
fee[a][b] = fee[b][a] =fe;
}
else if(dist[a][b] == len) fee[a][b] = fee[b][a] = min(fee[a][b], fe);//路径相同时,收费取最小的
}
Dijstra(s,d);
return 0;
}
将Dijkstra算法换成Floyd算法也能AC
void Floyd(int s,int b) //s为起点城市,b为终点城市
{
for(int t = 0; t < n; t ++)//t为path路径上的点
{
for(int i = 0; i < n; i ++)//i为起点
{
for(int j = 0; j < n; j ++)//j为终点
{
if(i != j &&dist[i][j] > dist[i][t] + dist[t][j])//i通过t到达j 比 i直接到j 更短
{
dist[i][j] = dist[j][i] = dist[i][t] + dist[t][j];//由于是无向图,两个方向都要更新
fee[i][j] = fee[j][i] = fee[i][t] + fee[t][j];
}
else if(dist[i][j] == dist[i][t] + dist[t][j])
fee[i][j] = min(fee[i][j], fee[i][t] + fee[t][j]);//路径相同取收费最小
else continue;
}
}
}
cout << dist[s][b] << ' ' << fee[s][b];
}
再单拎出Dijkstra算法
void Dijstra(int s,int b) //s为起点城市,b为终点城市
{
dist[s][s] = 0; //初始化
for(int i = 0; i < n; i ++)//一共遍历n次
{
int t = -1;
for(int j = 0; j < n; j ++)
{
if(!st[j] && (t == -1 || dist[s][t] > dist[s][j]))
{
t = j;//调用一个没有调用过的中间顶点
}
}
st[t] = true;//某点被用过则标记以下
for(int j = 0; j < n; j ++)//遍历所有点
{
if(dist[s][j] > dist[s][t] + dist[t][j]) //源点s通过t到达j 比 s直接到j 更短
{
dist[s][j] = dist[s][t] + dist[t][j]; //更新s到j的最短路径
fee[s][j] = fee[s][t] + fee[t][j]; //更新收费额
}
else if(dist[s][j] == dist[s][t] + dist[t][j])
fee[s][j] = min(fee[s][j], fee[s][t] + fee[t][j]);//路径相等时取收费最小的
else continue;
}
}
cout << dist[s][b] << ' ' << fee[s][b];
}
可以作一个对比观察。
如果还要求路径经过的城市,可以多增加一个path邻接矩阵来存储。
另外,这两个算法可以作为一个代码模板,类似的题基本可以一个模板过。