CF721C Journey 题解
题目给了我们一张 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;
}
}