问题描述:
给定一个带权有向图G = (V, E), 其中每条边的权是非负实数。另外,还给定V中的一个顶点,称为源。现在要计算源到所有其他各定点的最短长度。这里路的长度是指路上各边权之和。这个问题通常成为单源最短路径问题。
算法基本思想:
设置顶点集合S并不断的做贪心选择扩充这个集合。S存放已经找到最短路的点的集合。
算法流程:
(1)设置一集合S存放已经找到最短路的点的集合。
(2) 将源点并入S, 初始化源到V-S中各点的“当前最短路径”
(3) 重复如下步骤n-1次:
① 在V-S中找当前路径最短的顶点;
②并入S;
③ 据此更新源到其他点的距离。
注:
visited[i]标记i号顶点是否已并入S
设辅助数组dist, dist[i]存储源到顶点 i 的当前最短路长度
pre[v] 记录的是从源到顶点i的最短路径上i的前一个顶点。用来求出相应的最短路径,其初始化为源即pre[1:n] = p.
输入:
1 5 7
1 2 10
1 5 100
1 4 30
2 3 50
3 5 10
4 3 20
4 5 60
输出:
点 1 到点 2 的最小距离为 10
路径为 :
1 2
点 1 到点 3 的最小距离为 50
路径为 :
1 4 3
点 1 到点 4 的最小距离为 30
路径为 :
1 4
点 1 到点 5 的最小距离为 60
路径为 :
1 4 3 5
有向图: (画的有点丑....)
代码:
#include <bits/stdc++.h>
using namespace std;
const int max_ = 0x3f3f3f;
int Graph[110][110];
int dist[110]; //dist[i]存储源到顶点 i 的当前最短路长度
int visited[110]; //标记i号顶点是否已并入S
int pre[110]; //记录的是从源到顶点i的最短路径上i的前一个顶点。用来求出相应的最短路径
int m, n, p; //m代表路径的个数, n代表顶点的个数,p代表源
//回溯输出路径
void traceback(int i)
{
if(pre[i] == i)
{
printf("%d", i);
}
else
{
traceback(pre[i]);
printf(" %d", i);
}
}
void outPut()
{
for(int i = 1; i <= n; ++i)
{
if(i != p)
{
printf("点 %d 到点 %d 的最小距离为 %d\n", p, i, dist[i]);
cout << "路径为 :" << endl;
traceback(i);
cout << endl;
}
}
}
void Dijkstra()
{
//初始化
for(int i = 1; i <= n; ++i)
{
dist[i] = Graph[p][i];
visited[i] = 0;
pre[i] = p;
}
dist[p] = 0;
visited[p] = 1;
for(int i = 1; i < n; ++i)
{
int pos, min_len = max_;
for(int i = 1; i <= n; ++i)
{
if(!visited[i] && min_len > dist[i])
{
pos = i, min_len = dist[i];
}
}
visited[pos] = 1;
for(int j = 1; j <= n; ++j)
{
if(!visited[j] && dist[j] > min_len + Graph[pos][j])
{
dist[j] = min_len + Graph[pos][j];
pre[j] = pos;
}
else
continue;
}
}
}
void inPut()
{
int pos1, pos2, dis;
memset(Graph, max_, sizeof(Graph));
scanf("%d %d %d", &p, &n, &m);
// cout << "p = " << p << " n = " << n << " m = " << m << endl;
for(int i = 1; i <= m; ++i)
{
scanf("%d %d %d", &pos1, &pos2, &dis);
Graph[pos1][pos2] = dis;
}
}
int main()
{
inPut();
Dijkstra();
outPut();
}
运行截图: