:认识最短路径算法
四种常见的最短路径算法:
- 所有顶点间的最短路径算法 ---
- 单源最短路径算法 ---
- 判断负权回路最短路径算法 ---
- 带负权边最短路径算法 ---
最短路径:在带权有向图中 $A$ 点(源点)到 $B$ 点(终点)的多条路径中,寻找一条各边权值最小的路径,即**最短路径**。
:
**求最短路径步骤**
- 初始时设置一个 阶矩阵,令其对角线元素为 ,若存在边 ,则对应元素为权值;否则为正无穷。
- 逐步试着在原直接路径中添加中间顶点,若加入中间,后路径变短,则修改;否则维持原值。( 思想)。
- 所有顶点试探完毕,算法结束。
:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e2 + 10;
double g[N][N];
int x[N], y[N];
int n, m, u, v, s, t;
void floyd() {
for (int k = 1; k <= n; k++) {
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
g[i][j] = min(g[i][j], g[i][k] + g[k][j]);
}
}
}
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
memset(g, 127, sizeof(g));
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> x[i] >> y[i];
}
cin >> m;
while (m--) {
cin >> u >> v;
double dis = sqrt((x[u] - x[v]) * (x[u] - x[v]) + (y[u] - y[v]) * (y[u] - y[v]));
g[u][v] = g[v][u] = dis;
}
floyd();
cin >> s >> t;
cout << fixed << setprecision(2) << g[s][t] << "\n";
return 0;
}
:
**基本思想**:
将图中所有顶点分成两组:已知确定最短路 $S$、尚未确定最短路 $V-S$不断从第 $2$ 组中选择路径长度最短的点放入第 $1$ 组并扩展。
若顶点序列 是从 到 的最短路,则序列 必为从 到 的最短路。
**其本质为:贪心算法,且只能用于正权图。**
可以用优先队列 _ 和邻接表来优化。
:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e4 + 10;
int h[N], vtx[2 * N], nxt[2 * N], wt[2 * N], idx;
int dis[N], pre[N], vis[N];
int n, m, s, t, tu, tv, tw;
struct node {
int u, v, w;
friend bool operator < (node a, node b) {
if (a.w >= b.w) {
return 1;
} else {
return 0;
}
}
};
priority_queue <node> q;
void addEdge(int a, int b, int c) {
vtx[idx] = b;
nxt[idx] = h[a];
wt[idx] = c;
h[a] = idx++;
}
void dijkstra(int s) {
dis[s] = 0;
node tmp;
tmp.u = s;
tmp.v = s;
tmp.w = 0;
q.push(tmp);
while (!q.empty()) {
tmp = q.top();
q.pop();
int nid = tmp.v;
if (vis[nid] == 1) {
continue;
}
vis[nid] = 1;
int p = h[nid];
while (p != -1) {
if (vis[vtx[p]] == 0) {
if (dis[nid] + wt[p] < dis[vtx[p]]) {
dis[vtx[p]] = dis[nid] + wt[p];
pre[vtx[p]] = nid;
tmp.u = nid;
tmp.v = vtx[p];
tmp.w = dis[vtx[p]];
q.push(tmp);
}
}
p = nxt[p];
}
}
}
int main() {
memset(h, -1, sizeof(h));
cin >> n >> m >> s >> t;
for (int i = 1; i <= n; i++) {
dis[i] = 2147483647;
}
while (m--) {
cin >> tu >> tv >> tw;
if (tu != tv) {
addEdge(tu, tv, tw);
}
}
dijkstra(s);
return 0;
}
:(不需要存图,只要存边)
**主要算法思想**:
算法基于动态规划,反复用已有的边来更新最短距离。
- 每次利用所有的边,更新所有的结点。
- 重复这种操作 次( 为顶点个数)。如果更新了 次,仍然有顶点可以被更新,则表明存在负权环,最短路径不存在。
- 显然, 算法的时间复杂度为 ,复杂度要比 算法高得多。
**操作**:
- 记 和 是源点到 和 的距离, 是 到 的距离;
- 若 和 满足 ,则 的值更新成 ;
- 反复利用上式对 数组进行操作,如果没有负权回路的话,应当会在 次操作之后结束。
- 如果存在负权回路,那么第 次操作时仍然会成功,这时最短路径为负无穷。
:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e3 + 10;
int dis[N];
struct edge {
int u, v, w;
}e[2 * N];
int n, m, s, t;
void ford() {
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (dis[e[i].v] >= dis[e[i].u] + e[i].w) {
dis[e[i].v] = dis[e[i].u] + e[i].w;
}
}
}
}
int main() {
cin >> n >> m >> s >> t;
for (int i = 1; i <= n; i++) {
dis[i] = 2147483647;
}
for (int i = 1; i <= m; i++) {
cin >> e[i].u >> e[i].v >> e[i].w;
}
dis[s] = 0;
ford();
return 0;
}