题意
给你n
个点、m
条有向边的图,边有长度,现在要从1
号点走向n
号点,希望在总路径不超过T
的情况下经过尽可能多的点。题目保证有解,输出经过的点的方案。
想法
二维spfa,后来理解了一下其实就是DAG上的dp。f[i][j]
表示到了点i
,已经经过了j
个点的最小距离。对于f[i][j]
这个状态,肯定是经过的路径越小越好,因为题目要求总路径不能超过T
,因此是无后效性的。转移的过程也就是spfa的过程,这也就是“二维spfa”这个名字的由来吧。
Code
#include <cstdio>
#include <cstdlib>
#include <queue>
#include <cstring>
#include <iostream>
#define ll long long
#define db double
#define N 5011
#define mk make_pair
#define max(a, b) ((a) > (b) ? (a) : (b))
#define min(a, b) ((a) < (b) ? (a) : (b))
#define swap(T, a, b) ({T ttt = a; a = b; b = ttt;})
using namespace std;
int head[N], l = 0;
struct Edge { int from, to, len, next; } f[N];
int n, m, cnt = 0, ans[N], pre[N][N];
int T, d[N][N];
bool vis[N][N];
queue < pair<int, int> > q;
void spfa()
{
memset(d, 127, sizeof(d));
memset(vis, false, sizeof(vis));
d[1][0] = 0; vis[1][0] = true; pre[1][0] = 0;
q.push(mk(1, 0));
while (!q.empty())
{
int x = q.front().first, y = q.front().second; q.pop();
vis[x][y] = false;
for (int i = head[x]; i; i = f[i].next)
if (d[x][y] + f[i].len < d[f[i].to][y+1] && d[x][y] + f[i].len <= T)
{
d[f[i].to][y+1] = d[x][y] + f[i].len;
pre[f[i].to][y+1] = x;
if (!vis[f[i].to][y+1])
{
vis[f[i].to][y+1] = true;
q.push(mk(f[i].to, y+1));
}
}
}
}
int main()
{
scanf("%d%d%d", &n, &m, &T);
int u, v, z;
for (int i = 1; i <= m; i++)
{
scanf("%d%d%d", &u, &v, &z);
f[++l] = (Edge) {u, v, z, head[u]}; head[u] = l;
}
spfa(); int k = 1;
for (int i = n; i >= 1; i--)
if (d[n][i] <= T) { k = i; break; }
printf("%d\n1 ", k+1);
for (int x = n, y = k; x > 0 && y > 0; x = pre[x][y--])
ans[++cnt] = x;
for (int i = cnt; i >= 1; i--)
printf("%d ", ans[i]);
return 0;
}