题目给了我们一张 DAG,对于 DAG 常用的方法就是拓扑排序。
题目要求一条从 1 到 \(n\) 的路径, 点数尽量多但是距离不能超过 \(k\),那么我们考虑 DP 解决这个问题。
设 \(f_{i,j}\) 表示从 1 开始经过 \(i\) 个点,到达 \(j\) 点的最短路径,那么首先满足最优性。
而拓扑排序的一条重要性质就是只有入度为 0 的点才能处理,恰好满足无后效性。
于是我们就可以设计出这样的状态转移方程:
\[f_{i+1,v}=\min\{f_{i,u}+dis_{u->v}|u \in S\} \]\(S\) 表示所有能够到达 \(u\) 的点集。
初始状态为 \(f_{1,1}=0\),其余为正无穷。
同时为了记录方案,我们还需要一个 \(g_{i,j}\) 表示当前状态是由哪个点转移而来的。
最后输出时,从大到小枚举 \(i \in [1,n]\),找到第一个 \(f_{i,n} \ne \inf\) 就输出答案以及方案。
代码:
#include <bits/stdc++.h> using namespace std; typedef long long LL; const int MAXN = 5000 + 10; int n, m, f[MAXN][MAXN], g[MAXN][MAXN], t, cnt[MAXN]; vector <int> Next[MAXN], Num[MAXN]; int read() { int sum = 0, fh = 1; char ch = getchar(); while (ch < '0' || ch > '9') {if (ch == '-') fh = -1; ch = getchar();} while (ch >= '0' && ch <= '9') {sum = (sum << 3) + (sum << 1) + (ch ^ 48); ch = getchar();} return sum * fh; } void topsort() { queue <int> q; for (int i = 1; i <= n; ++i) if (!cnt[i]) q.push(i); while (!q.empty()) { int x = q.front(); q.pop(); for (int i = 0; i < Next[x].size(); ++i) { int u = Next[x][i], w = Num[x][i]; for (int j = 1; j <= n; ++j) { if (f[j][x] == 0x3f3f3f3f) continue; if (f[j][x] + w > t) continue; if (f[j + 1][u] > f[j][x] + w) {f[j + 1][u] = f[j][x] + w; g[j + 1][u] = x;} } --cnt[u]; if (cnt[u] == 0) q.push(u); } } } void print(int last, int now) { if (last > 1) print(last - 1, g[last][now]); printf("%d ", now); } int main() { n = read(), m = read(), t = read(); for (int i = 1; i <= m; ++i) { int x = read(), y = read(), z = read(); cnt[y]++; Next[x].push_back(y), Num[x].push_back(z); } memset(f, 0x3f, sizeof(f)); f[1][1] = 0; topsort(); for (int i = n; i >= 1; --i) if (f[i][n] < 0x3f3f3f3f) { printf("%d\n", i); print(i, n); printf("\n"); return 0; } }