# 停课竞赛第四天2020/10/29

## 最短路:

### 一、朴素版dijkstra

##### 时间复杂度: O ( n 2 ) O(n^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 1 3 3 的最短路为 1 1 ，但由于dijkstra的贪心思想，会在还没走 2 − 3 2-3 这条边之前就将1-2的路径更新了。

### 二、堆优化版dijkstra

##### 时间复杂度: O ( m l o g m ) O(mlogm)

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)

#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 ( n m ) O(nm)

#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);
}
if (spfa() == 0x3f3f3f3f) printf("impossible\n");
else printf("%d\n", dist[n]);
return 0;
}


### 五、Floyd

##### 例题

#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判断负环

#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);
}
if (spfa()) printf("Yes\n");
else printf("No\n");
return 0;
}


### 七、小结

今天的内容看起来很少，但是含量很足，每种算法都是求最短路的，但它们之前的使用范围和复杂度都是不同的，之后还要多刷题巩固。

11-14 1011
10-28 23

10-27 33
11-05 14
04-01 400