首先我们构造研究对象:
计算从V0开始到所有节点的最短路径
1、dijkstra,D算法
- 首先我们将需要计算最小路径的入口点的Cost复制到一个D数组里。(邻接矩阵对应的行)
- 我们知道第一个节点到达的各个顶点所需的花费(路程)(无法到达花费是正无穷)
- 找到最近的那个点。存下来(如果我要从已更新的点集中迈出第一步,那么我至少要走多远。)
- 我们有了两个已知的节点作为中介,当更新第三个点的时候,我们就有了一个中转节点。
- 我们比较直接到达和经过中转节点到达哪一个更近然后不停的迈出最小的步数,我们对每一个节点都计算这样的路径,并从这些路径中选择一个最小的加入到我们已知的点集之中。用一个数组来记录所有更新完成的状态D[i]表示到第i号节点的最短路径。(用已知的最短路径去更新子图)
- 所有节点均被使用后,算法运行完成。
- 查询:输入一个Aim,直接输出D[Aim]就可以得到结果。
总结下来就是,不停的更新D数组直到无法再次更新后,D数组就是我们想要的答案。如果有点懵,就来看算法。在大脑里跑一遍就明白了
经过优化后的算法(可以直接当做算法模板来使用。):
#include<iostream>
#include<algorithm>
#define inf 0x3f3f3f3f//表示正无穷
using namespace std;
int cost[6][6] = {//cost[i][j]表示从i到j的花费
inf, 12, 10,inf, 30,100,
inf,inf, 5,inf,inf,inf,
inf,inf,inf, 50,inf,inf,
inf,inf,inf,inf, 20, 10,
inf,inf,inf,inf,inf, 60,
inf,inf,inf,inf,inf,inf,
};
bool used[6];//核心部分开始
int D[6];
int V = 6;//大V表示点数
void Dijkstra(int s)
{
fill(D, D + V, inf);
fill(used, used + V, false);
D[s] = 0;//让v从s开始
while (true)
{
int v = -1;//小v为当前访问的点
for (int u = 0; u < V; u++)
{
if (!used[u] && (v == -1 || D[u] < D[v]))v = u;
}
if (v == -1)break;
used[v] = true;
for (int u = 0; u < V; u++) {
D[u] = min(D[u], D[v] + cost[v][u]);//中转到达和直达的花费取最小值,第一遍更新因为D[s]=0,算法就会将s号节点的Cost数组复制到D中
}
}
}//核心部分结束
int main()
{
Dijkstra(0);
for (int i = 0; i < V; i++)
{
cout << i << ":" << D[i] << endl;
}
int Aim;//查询
cin >> Aim;
cout << D[Aim] << endl;
}
D算法适合于方便构造Cost矩阵的情况
算法复杂度O(|V|)
2、Bellma-Ford算法
这个算法更加典型,他适合于知道边集而不方便构造邻接矩阵的情况。
- 对边进行遍历
- 比较直达和中转的路径花费,取最小值填进去。
void Bellman_ford(int s)
{
bool upgrade = false;
fill(D, D + V, inf);
D[s] = 0;
while (true)
{
for (int i = 0; i < E; i++)
{
edge e = es[i];
if (D[e.from] != inf && D[e.to] > D[e.from] + e.cost)
{
D[e.to] = D[e.from] + e.cost;
upgrade = true;
}
}
if (!upgrade) break;//如果没有更新就break
}
}
算法复杂度 O(|V|*|E|)。