R語言進行分析,比較詳細的一篇,親測過哦
最短路問題
樸素版Dijkstra(稠密圖)
s陣列,當前已經確定最短路徑的點
1 dis[1] = 0, dis[i] = 正無窮
2 for(int i = 0; i <= n; i ++)
t <- 不在s中距離最近的點
s <- t 用t更新其他點的距離(從t出去的所有的邊組成的路徑能不能更新其 他點的距離
1 -> t -> x;
1 ------> x;如果前一種情況的距離小於第二種就可以更新
dis[x] > dis[t] + w;
給定一個 n 個點 m 條邊的有向圖,圖中可能存在重邊和自環,所有邊權均為正值。
請你求出 1 號點到 n 號點的最短距離,如果無法從 1 號點走到 n 號點,則輸出 −1。
輸入格式
第一行包含整數 n 和 m。
接下來 m 行每行包含三個整數 x, y, z,表示存在一條從點 x 到點 y 的有向邊,邊長為 z。
輸出格式
輸出一個整數,表示 1 號點到 n 號點的最短距離。
如果路徑不存在,則輸出 −1。
資料範圍
1 ≤ n ≤ 500,
1 ≤ m ≤ 105,
圖中涉及邊長均不超過10000。
輸入樣例:
3 3
1 2 2
2 3 1
1 3 4
輸出樣例:
3
#include <bits/stdc++.h> using namespace std; const int N = 510; int g[N][N]; int dist[N]; bool st[N]; int n, m; int dijkstra(){ memset(dist, 0x3f, sizeof dist); dist[1] = 0; for(int i = 0; i < n; i ++) { int t = -1; for(int j = 1; j <= n; j ++) if(!st[j] && (t == -1 || dist[t] > dist[j])) t = j; st[t] = true; for(int j = 1; j <= n; j ++){ dist[j] = min(dist[j], dist[t] + g[t][j]); } } if(dist[n] == 0x3f3f3f3f) return -1; return dist[n]; } int main(){ cin >> n >> m; memset(g, 0x3f, sizeof g); while(m --){ int a, b, c; cin >> a >> b >> c; g[a][b] = min(g[a][b], c); } cout << dijkstra(); return 0; }
堆優化版Dijkstra(稀疏圖)
給定一個 n 個點 m 條邊的有向圖,圖中可能存在重邊和自環,所有邊權均為非負值。
請你求出 1 號點到 n 號點的最短距離,如果無法從 1 號點走到 n 號點,則輸出 −1−1。
輸入格式
第一行包含整數 n 和 m。
接下來 mm 行每行包含三個整數 x,y,z表示存在一條從點 x 到點 y 的有向邊,邊長為 z。
輸出格式
輸出一個整數,表示 1號點到 n 號點的最短距離。
如果路徑不存在,則輸出 −1。
資料範圍
1≤n,m≤1.5×105
圖中涉及邊長均不小於 0,且不超過 10000。
資料保證:如果最短路存在,則最短路的長度不超過 109。
輸入樣例:
3 3 1 2 2 2 3 1 1 3 4
輸出樣例:
3
#include <cstring>
#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
typedef pair<int, int> PII;
const int N = 1e6 + 10;
int n, m;
int h[N], w[N], e[N], ne[N], idx;
int dist[N];
bool st[N];
void add(int a, int b, int c)
{
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
}
int dijkstra()
{
memset(dist, 0x3f, sizeof dist);
dist[1] = 0;
priority_queue<PII, vector<PII>, greater<PII>> heap;
heap.push({0, 1});//前一個是距離,後一個是
while (heap.size())
{
auto t = heap.top();
heap.pop();
int ver = t.second, distance = t.first;
if (st[ver]) continue;
st[ver] = true;
for (int i = h[ver]; i != -1; i = ne[i])
{
int j = e[i];
if (dist[j] > dist[ver] + w[i])
{
dist[j] = dist[ver] + w[i];
heap.push({dist[j], j});
}
}
}
if (dist[n] == 0x3f3f3f3f) return -1;
return dist[n];
}
int main()
{
scanf("%d%d", &n, &m);
memset(h, -1, sizeof h);
while (m -- )
{
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
add(a, b, c);
}
cout << dijkstra() << endl;
return 0;
}
Bellman_Ford(極少數情況,圖中不存在負權迴路(負環))
1.for(int i = 1; i <= n; i ++)
2.備份dist防止串聯
3.for 所有邊 a,b,w
4.dist[b] = min(dist[b], dist[a] + w)(鬆弛操作)
最後滿足dist[b] <= dist[a] + w(三角不等式)
此演算法不能存在負權迴路
是否存在負權迴路的驗證方法
1.迭代K次(不超過k條邊)的最短路的距離
2.如果第n次迭代時更新了某些邊(說明存在一條最短路徑有n條邊,n+1個點)(說明一定存在負權迴路)
一共有n個點,但是出現了n個邊,根據抽屜原理,說明一定有迴路存在
有邊數限制的最短路
給定一個 n 個點 m 條邊的有向圖,圖中可能存在重邊和自環, 邊權可能為負數。
請你求出從 1 號點到 n 號點的最多經過 k 條邊的最短距離,如果無法從 1 號點走到 n 號點,輸出 impossible
。
注意:圖中可能 存在負權迴路 。
輸入格式
第一行包含三個整數 n,m,k。
接下來 m 行,每行包含三個整數 x,y,z,表示存在一條從點 x 到點 y 的有向邊,邊長為 z。
輸出格式
輸出一個整數,表示從 1 號點到 n 號點的最多經過 k 條邊的最短距離。
如果不存在滿足條件的路徑,則輸出 impossible
。
資料範圍
1≤n,k≤500,
1≤m≤10000,
任意邊長的絕對值不超過 10000。
輸入樣例:
3 3 1
1 2 1
2 3 1
1 3 3
輸出樣例:
3
#include <bits/stdc++.h>
using namespace std;
const int N = 510, M = 10010;
struct Edge
{
int a, b, w;
}edges[M];
int dist[N];
int backup[N];
int n, m, k;
void bellman_ford(){
memset(dist, 0x3f, sizeof dist);
dist[1] = 0;
for(int i = 0; i < k; i ++ ){
memcpy(backup, dist, sizeof dist);
for(int j = 0; j < m; j ++){
auto t = edges[j];
dist[t.b] = min(dist[t.b], backup[t.a] + t.w);
}
}
}
int main(){
cin >> n >> m >> k;
for(int i = 0; i < m; i ++){
int a, b, w;
cin >> a >> b >> w;
edges[i] = {a, b, w};
}
bellman_ford();
if(dist[n] > 0x3f3f3f3f / 2) puts("impossible");
else cout << dist[n] << endl;
return 0;
}