题目地址:
https://www.acwing.com/problem/content/855/
给定一个
n
n
n个点
m
m
m条边的有向图,图中可能存在重边和自环,边权可能为负数。请你求出从
1
1
1号点到
n
n
n号点的最多经过
k
k
k条边的最短距离,如果无法从
1
1
1号点走到
n
n
n号点,输出impossible
。
输入格式:
第一行包含三个整数
n
n
n,
m
m
m,
k
k
k。接下来
m
m
m行,每行包含三个整数
x
x
x,
y
y
y,
z
z
z,表示存在一条从点
x
x
x到点
y
y
y的有向边,边长为
z
z
z。
输出格式:
输出一个整数,表示从
1
1
1号点到
n
n
n号点的最多经过
k
k
k条边的最短距离。如果不存在满足条件的路径,则输出impossible
。
数据范围:
1
≤
n
,
k
≤
500
1\le n,k\le 500
1≤n,k≤500
1
≤
m
≤
10000
1\le m\le 10000
1≤m≤10000
0
≤
∣
e
∣
≤
10000
0\le |e|\le 10000
0≤∣e∣≤10000
e
e
e是边长
有边数限制的最短路只能用Bellman-Ford算法来做。它的算法步骤是,循环 k k k次,每次循环内部,再循环每条边,用这条边来尝试更新这条边入点的最短路。注意,在进行内部循环之前,要对dist数组做一个备份,以防dist数组是以当前内循环已经更新过的数值来更新的(外循环实际上是限制了最多经过多少条边。如果不备份的话,内层循环算出来的最短路有可能是超过外层循环的限制边数的)。这个算法的思路其实是动态规划,设 f [ k ] [ x ] f[k][x] f[k][x]是从 1 1 1号点走不超过 k k k条边走到 x x x的所有路径里的最短路,初始条件是 f [ 0 ] [ 1 ] = 0 f[0][1]=0 f[0][1]=0,按照 x x x的上一个点是谁可以做分类,则有: f [ k ] [ x ] = min y → x { f [ k − 1 ] [ y ] + d [ y ] [ x ] } f[k][x]=\min_{y\to x}\{f[k-1][y]+d[y][x]\} f[k][x]=y→xmin{f[k−1][y]+d[y][x]}每轮循环枚举所有的边,最后要求的就是 f [ k ] [ n ] f[k][n] f[k][n]。代码如下:
#include <iostream>
#include <cstring>
using namespace std;
const int N = 510, M = 10010, INF = 0x3f3f3f3f;
int n, m, k;
struct Edge {
int u, v, w;
} e[M];
int dist[2][N];
int bellman_ford() {
memset(dist, 0x3f, sizeof dist);
dist[0][1] = dist[1][1] = 0;
for (int i = 1; i <= k; i++)
for (int j = 1; j <= m; j++) {
int u = e[j].u, v = e[j].v, w = e[j].w;
dist[i & 1][v] = min(dist[i & 1][v], dist[i - 1 & 1][u] + w);
}
if (dist[k & 1][n] > INF / 2) return INF;
else return dist[k & 1][n];
}
int main() {
scanf("%d%d%d", &n, &m, &k);
for (int i = 1; i <= m; i++) {
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
e[i] = {u, v, w};
}
int res = bellman_ford();
res == INF ? puts("impossible") : printf("%d\n", res);
}
时间复杂度 O ( n m ) O(nm) O(nm),空间 O ( n + m ) O(n+m) O(n+m)。