停課競賽第三天2020/10/29
最短路:
一、樸素版dijkstra
合適的使用範圍:無負權邊的稠密圖
演算法複雜度: O ( n 2 ) O(n^2) O(n2)
實現方式:按點更新,用當前最近的沒有更新到的點更新其他沒更新到的點。第一層列舉
n
n
n次,第二層1判斷最近的點,第二層2更新其他還沒更新到的點。
程式碼實現:
int dijkstra() {
memset(dist, 0x3f, sizeof dist);
dist[1] = 0;
for (int i = 1; i < n; i++) {
int t = -1;
for (int j = 1; j <= n; j++)
if (!st[j] && (t == -1 || dist[j] < dist[t])) 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];
}
如果有負權邊,則可能使得最短路比原來的長。舉例:
1
−
2
−
4
,
1
−
3
−
5
,
2
−
3
−
(
−
3
)
1-2-4,1-3-5,2-3-(-3)
1−2−4,1−3−5,2−3−(−3)。可以發現
1
1
1 到
3
3
3 的最短路為
1
1
1 ,但由於dijkstra的貪心思想,會在還沒走
2
−
3
2-3
2−3 這條邊之前就將1-2的路徑更新了。
二、堆優化版dijkstra
合適的使用範圍:無負權邊的稀疏圖
演算法複雜度: O ( m l o g m ) O(mlogm) O(mlogm)
實現方式:與樸素版dijkstra相似,但是,是用最短邊去更新沒有更新到的點。第一層列舉最短邊,第二層更新沒有更新到的點。
程式碼實現:
struct node {
int x, y;
bool operator < (const node &a) const {
return y > a.y;
}
};
int n, m;
int h[N], e[N], w[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));
priority_queue<node> q;
dist[1] = 0;
q.push({1, 0});
while (q.size()) {
node t = q.top();
q.pop();
if (st[t.x]) continue;
for (int i = h[t.x]; i != -1; i = ne[i]) {
if (dist[e[i]] > dist[t.x] + w[i]) {
dist[e[i]] = dist[t.x] + w[i];
q.push({e[i], dist[e[i]]});
}
}
st[t.x] = true;
}
if (dist[n] == 0x3f3f3f3f) return -1;
else return dist[n];
}
同樣如果有負權邊,則無法判斷是否存在最短路。
三、Bellman-ford
合適的使用範圍:有負權邊且有最短路邊數限制的圖(可以判斷負環)
演算法複雜度: O ( n m ) O(nm) O(nm)
實現方式:第一次列舉
n
n
n次,第二次列舉
m
m
m條邊,可以證明n次迭代後,如果有最短路必能求出,列舉k次即k條邊的最短路。
注意:當用該演算法求k條邊的最短路時要有備份backup[n],原因在於在一次
n
n
n 的列舉中不能用已經更新過的點去更新其他點。最後判斷有無最短路時要用已得距離與一個較大的數進行比較。
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 510, M = 10010;
struct Edge {
int a, b, c;
} edge[M];
int n, m, k;
int dist[N], backup[N];
int 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++)
if (dist[edge[j].b] > backup[edge[j].a] + edge[j].c)
dist[edge[j].b] = backup[edge[j].a] + edge[j].c;
}
if (dist[n] > 0x3f3f3f3f / 2) return -1;
else return dist[n];
}
int main() {
scanf("%d %d %d", &n, &m, &k);
for (int i = 0; i < m; i++) {
int a, b, c;
scanf("%d %d %d", &a, &b, &c);
edge[i].a = a, edge[i].b = b, edge[i].c = c;
}
if (Bellman_ford() == -1) printf("impossible\n");
else printf("%d\n", dist[n]);
return 0;
}
四、SPFA
合適的適用範圍:有負權邊且沒最短路邊數的限制的圖(可以判斷負環)
演算法複雜度:一般 O ( m ) O(m) O(m),最壞 O ( n m ) O(nm) O(nm)
實現方式:與Bellman_ford演算法相似,不同之處在於要用一個佇列來儲存改變了值的點,節點的值沒變的則不列舉。所以,該演算法也與堆優化的dijkstra演算法十分相似。
#include <cstdio>
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
const int N = 1e5 + 10;
int n, m;
int h[N], e[N], w[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 spfa() {
memset(dist, 0x3f, sizeof(dist));
dist[1] = 0;
queue<int> q;
q.push(1);
st[1] = true;
while (q.size()) {
int t = q.front();
q.pop();
st[t] = false;
for (int i = h[t]; i != -1; i = ne[i]) {
int j = e[i];
if (dist[j] > dist[t] + w[i]) {
dist[j] = dist[t] + w[i];
if (!st[j]) {
q.push(j);
st[j] = true;
}
}
}
}
return dist[n];
}
int main() {
memset(h, -1, sizeof(h));
scanf("%d %d", &n, &m);
for (int i = 0; i < m; i++) {
int a, b, c;
scanf("%d %d %d", &a, &b, &c);
add(a, b, c);
}
if (spfa() == 0x3f3f3f3f) printf("impossible\n");
else printf("%d\n", dist[n]);
return 0;
}
五、Floyd
演算法實現:動態規劃,列舉中間點,中間點首先列舉,可以有負權邊,但不能有負環。
例題
給定一個n個點m條邊的有向圖,圖中可能存在重邊和自環,邊權可能為負數。
再給定k個詢問,每個詢問包含兩個整數x和y,表示查詢從點x到點y的最短距離,如果路徑不存在,則輸出“impossible”。
資料保證圖中不存在負權迴路。
#include <cstring>
#include <iostream>
using namespace std;
const int N = 210, INF = 1e9;
int n, m, Q;
int d[N][N];
void floyd() {
for (int k = 1; k <= n; k ++ )
for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= n; j ++ )
d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
}
int main(){
scanf("%d%d%d", &n, &m, &Q);
for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= n; j ++ )
if (i == j) d[i][j] = 0;
else d[i][j] = INF;
while (m -- ) {
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
d[a][b] = min(d[a][b], c);
}
floyd();
while (Q -- ) {
int a, b;
scanf("%d%d", &a, &b);
int t = d[a][b];
if (t > INF / 2) puts("impossible");
else printf("%d\n", t);
}
return 0;
}
六、SPFA判斷負環
實現方式:根據SPFA的實現原理我們可以知道當所有的點都無法更新的時候演算法就會結束了,但如果有負環的話則會導致程式無法結束。根據抽屜原理我們可以知道,一個有 n n n 個節點的圖,如果沒出現環的話,那麼從一個點到另一個的最短路的最多經過 n − 1 n - 1 n−1個點,所以我們可以根據轉移數量來判定一個圖中有沒有負環。
#include <cstdio>
#include <iostream>
#include <queue>
#include <cstring>
using namespace std;
const int N = 1e5 + 10;
int n, m;
int h[N], e[N], ne[N], w[N], idx;
int dist[N];
bool st[N];
int cnt[N];
void add(int a, int b, int c) {
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}
int spfa() {
queue<int> q;
for (int i = 1; i <= n; i++) {
st[i] = true;
q.push(i);
}
while (q.size()) {
int t = q.front();
q.pop();
st[t] = false;
for (int i = h[t]; i != -1; i = ne[i]) {
int j = e[i];
if (dist[j] > dist[t] + w[i]) {
dist[j] = dist[t] + w[i];
cnt[j] = cnt[t] + 1;
if (cnt[j] >= n) return true;
if (!st[j]) {
st[j] = true;
q.push(j);
}
}
}
}
return false;
}
int main() {
memset(h, -1, sizeof(h));
scanf("%d %d", &n, &m);
for (int i = 0; i < m; i++) {
int a, b, c;
scanf("%d %d %d", &a, &b, &c);
add(a, b, c);
}
if (spfa()) printf("Yes\n");
else printf("No\n");
return 0;
}
七、小結
今天的內容看起來很少,但是含量很足,每種演算法都是求最短路的,但它們之前的使用範圍和複雜度都是不同的,之後還要多刷題鞏固。
相關文章
- 2020 10 29日
- 2020-10-29
- 2020藍橋杯競賽複習指導
- 資訊學競賽免費課程之C++語法入門網課影片C++
- CSDN周賽第29期:贏實體書《演算法競賽》和定製周邊等禮品演算法
- 課時29.錨點(掌握)
- 2020-10-29 org.apache.commons.lang3.StringUtilsApache
- 2020-09-29
- 2020-12-29
- 2020-11-29
- 網易電競NeXT2020春季賽火力全開!線上賽升級開啟競技狂歡
- 停課日誌part2
- 2024/10/29
- 10月29
- 2020“數維杯”國際大學生數學建模競賽賽題分析
- 10衝刺第三天
- 網易電競NeXT2020春季賽4.3火力全開!線上賽升級開啟競技狂歡
- 競由自我,無以為界——2020網易電競NeXT冬季賽今日開戰!
- 2020,成都遊戲生態圈上演的軍備競賽遊戲
- 10天衝刺第三天
- 課時29:檔案:一個任務
- 課後練習-登入-2024/9/29
- 來了!阿里公佈全球數學競賽決賽名單 張益唐將授課大師班阿里
- java學習第三天2020/7/8Java
- Spring AOP2020-09-29Spring
- 2020-11-29日誌
- 守望先鋒聯賽2020賽季籌備開戰,它會成就電競賽事的新歷史嗎?
- 北美競賽-加拿大計算機競賽CCC-收穫滑鐵盧計算機
- 競無限速,S聯賽2019年春季賽今日開賽
- 2020_9_29_集合和字串字串
- 團隊10天衝刺第三天
- 資料競賽:第四屆工業大資料競賽-虛擬測量大資料
- 10月29日作業
- 2024年10月29日
- 富士通加入AI競賽AI
- 資料競賽Tricks集錦
- 演算法競賽小技巧演算法
- 演算法競賽日誌演算法