算法:又称为插点法,是一种利用动态规划的思想寻找给定的加权图中多源点之间最短路径的算法,与 算法类似
核心思路
路径矩阵
通过一个图的权值矩阵求出它的每两点间的最短路径矩阵。
从图的带权邻接矩阵 开始,递归地进行n次更新,即由矩阵 ,按一个公式,构造出矩阵 ;又用同样地公式由 构造出 ;……;最后又用同样的公式由 构造出矩阵 。矩阵 的 行 列元素便是 号顶点到 号顶点的最短路径长度,称 为图的距离矩阵,同时还可引入一个后继节点矩阵 来记录两点间的最短路径。
采用松弛技术(松弛操作),对在i和j之间的所有其他点进行一次松弛。所以时间复杂度为
状态转移方程
其状态转移方程如下: = { };
表示 到 的最短距离, 是穷举 的断点, 初值应该为0,或者按照题目意思来做。
当然,如果这条路没有通的话,还必须特殊处理,比如没有 这条路。
关于 算法理解起来还是比较简单的,按照上面的路径矩阵来说, 表示初始矩阵, 则可以这样理解,即加入某个中间点,例如上图中加入一个 点得到 ,这个 代表的是加入 点之后路径更新之后的矩阵,就比如说原来 没有直接路径到达 ,而加入 点之后 就可以通过 点到达 点,加入的点就相当于中间点
算法实现
在这里直接用权值矩阵代替路径矩阵,就不另设一个; 同时代码实现求出上图所有顶点之间的最短路径
(1) 实现准备:
#define MAXVEX 100
#define INFINITY 65535
int map[MAXVEX][MAXVEX]; //权值矩阵,即邻接矩阵
int p[MAXVEX][MAXVEX]; //后继节点路径
char vex[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i' };
(2) 初始化节点矩阵:
int i,j,k;
for (i = 0; i < n; ++i) {
for (j = 0; j < n; ++j) {
p[i][j] = j;
}
}
(3) 算法核心:
for (k = 0; k < n; ++k) {
for (i = 0; i < n; ++i) {
for (j = 0; j < n; ++j) {
//转移方程:如果经过k顶点路径比原两点间路径更短则更新
if (map[i][j] > map[i][k] + map[k][j]) {
map[i][j] = map[i][k] + map[k][j];
p[i][j] = p[i][k];
}
}
}
}
(4) 打印路径:
for (i = 0; i < n; ++i) {
for (j = i + 1; j < n; ++j) {
cout << vex[i] << "->" << vex[j] << " minimum sum weight: " << map[i][j] << endl;
int t = p[i][j];
cout << "path: " << vex[i];
while (t != j) {
cout << "->" << vex[t];
t= p[t][j];
}
cout << "->" << vex[t] << endl;
}
cout << endl;
}
完整实现:
#include <iostream>
#include <fstream>
using namespace std;
#define MAXVEX 100
#define INFINITY 65535
int map[MAXVEX][MAXVEX];
int p[MAXVEX][MAXVEX];
char vex[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i' };
void Floyd(int map[][MAXVEX], int n) {
int i,j,k;
for (i = 0; i < n; ++i) {
for (j = 0; j < n; ++j) {
p[i][j] = j;
}
}
for (k = 0; k < n; ++k) {
for (i = 0; i < n; ++i) {
for (j = 0; j < n; ++j) {
if (map[i][j] > map[i][k] + map[k][j]) {
map[i][j] = map[i][k] + map[k][j];
p[i][j] = p[i][k];
}
}
}
}
}
int main() {
int n,m;
int i,j,k,cost;
ifstream in("input.txt");
in >> n >> m;
for (i = 0; i < n; ++i) {
for (j = 0; j < n; ++j) {
map[i][j] = INFINITY;
}
}
for (k = 0; k < m; ++k) {
in >> i >> j >> cost;
map[i][j] = cost;
map[j][i] = cost;
}
Floyd(map, n);
for (i = 0; i < n; ++i) {
for (j = i + 1; j < n; ++j) {
cout << vex[i] << "->" << vex[j] << " minimum sum weight: " << map[i][j] << endl;
int t = p[i][j];
cout << "path: " << vex[i];
while (t != j) {
cout << "->" << vex[t];
t= p[t][j];
}
cout << "->" << vex[t] << endl;
}
cout << endl;
}
return 0;
}
9 15
0 1 10
0 5 11
1 2 18
1 6 12
1 7 12
2 3 22
2 7 8
3 4 20
3 6 24
3 7 21
3 8 16
4 5 26
4 8 7
5 6 17
6 8 19
a->b minimum sum weight: 10
path: a->b
a->c minimum sum weight: 28
path: a->b->c
a->d minimum sum weight: 43
path: a->b->h->d
a->e minimum sum weight: 37
path: a->f->e
a->f minimum sum weight: 11
path: a->f
a->g minimum sum weight: 22
path: a->b->g
a->h minimum sum weight: 22
path: a->b->h
a->i minimum sum weight: 41
path: a->b->g->ib->c minimum sum weight: 18
path: b->c
b->d minimum sum weight: 33
path: b->h->d
b->e minimum sum weight: 38
path: b->g->i->e
b->f minimum sum weight: 21
path: b->a->f
b->g minimum sum weight: 12
path: b->g
b->h minimum sum weight: 12
path: b->h
b->i minimum sum weight: 31
path: b->g->ic->d minimum sum weight: 22
path: c->d
c->e minimum sum weight: 42
path: c->d->e
c->f minimum sum weight: 39
path: c->b->a->f
c->g minimum sum weight: 30
path: c->b->g
c->h minimum sum weight: 8
path: c->h
c->i minimum sum weight: 38
path: c->d->id->e minimum sum weight: 20
path: d->e
d->f minimum sum weight: 41
path: d->g->f
d->g minimum sum weight: 24
path: d->g
d->h minimum sum weight: 21
path: d->h
d->i minimum sum weight: 16
path: d->ie->f minimum sum weight: 26
path: e->f
e->g minimum sum weight: 26
path: e->i->g
e->h minimum sum weight: 41
path: e->d->h
e->i minimum sum weight: 7
path: e->if->g minimum sum weight: 17
path: f->g
f->h minimum sum weight: 33
path: f->a->b->h
f->i minimum sum weight: 33
path: f->e->ig->h minimum sum weight: 24
path: g->b->h
g->i minimum sum weight: 19
path: g->ih->i minimum sum weight: 37
path: h->d->i
代码虽然比较简单,但非常巧妙!!!从代码来看,时间复杂度为