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;
		}
}
posted @ 2022-04-14 21:08  Plozia  阅读(34)  评论(0编辑  收藏  举报