DAG上的dp:Codeforces 721C Journey
题目描述
https://codeforces.com/problemset/problem/721/C
Irina来到了城市Berlatov。该城市中有n个景点,编号从1至n,其中一些景点被单向道路连接。图中没有环路。最初Irina站在景点1,她的旅行终点是景点n。Irina想要游玩尽可能多的景点,但是,Irina在这座城市中的停留时间是有限的,她不能在这里呆超过T时间。帮助Irina规划在不超过T时间内,她可以游玩多少个景点。保证在T时间内至少有一条路径可以从景点1到达景点n。
Input
第一行:n,m,T——景点数量,道路的数量,游玩时间限制(2 ≤ n ≤ 5000, 1 ≤ m ≤ 5000, 1 ≤ T ≤ 109)
下面m行,每行u,v,t从u到v有一条路,且该条道路需要花费t时间通过。保证道路不会形成回路。数据保证有解。
Output
第一行输出数字k(2 ≤ k ≤ n)——Irina最多可游玩的景点
第二行按游玩顺序输出k个不同的景点的编号
如果有多种方案,输出其中一种
Examples
input
4 3 13
1 2 5
2 3 7
2 4 8
output
3
1 2 4
input
6 6 7
1 2 2
1 3 3
3 6 3
2 4 2
4 6 2
6 5 1
output
4
1 2 4 6
input
5 5 6
1 3 3
3 5 3
1 2 2
2 4 3
4 5 2
output
3
1 3 5
题解
该题要求在时间限制内走尽量多的点,由此联想到01背包的模型,不难想到要在DAG上dp求解。
一般在DAG上的dp数组设置为d[i](必要时可多加维数),d[i]既可以定义成从i出发,也可以定义成以i结束,视题目类型而定。
本题中,我们定义d[i][j]表示以i为终点,从1经过j个点到达i点的路径长度,其中j是游玩景点的数量。
既然该图是有向无环图,意味着可以做一遍拓扑排序使经过的每个点只能往编号更大的点走,而不往编号更小的点走,这就可以让dp无后效性。
因此,我们只需要从1开始,做一遍拓扑排序,在排序的过程中不断地用当前的点u更新u指向的下一个点v的路径值,到u时经过了j-1个点的最短路径+u,v连边的边权小于d[v][j]时,就更新d[v][j],转移方程如下:
d[v][j]=min{d[u][j-1]+cost[u][v]} u->v
变量定义
in[i]:第i个点的入度
dp[i][j]:以i为终点,从1经过j个点到达
代码
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=5010; const int INF=0x3f3f3f3f; int n,m,T; struct node{ int to,next,cost; }e[maxn]; int dp[maxn][maxn]; int head[maxn],pos; int pre[maxn][maxn]; int st[maxn],top; int in[maxn]; void add(int x,int y,int z) { e[++pos].next=head[x]; e[pos].to=y; e[pos].cost=z; head[x]=pos; } queue<int> q; void solve() { for(int i=1;i<=n;i++) if(in[i]==0) q.push(i); while(!q.empty()) { int u=q.front();q.pop(); for(int i=head[u];i;i=e[i].next) { int v=e[i].to; in[v]--; if(in[v]==0) q.push(v); for(int j=2;j<=n;j++) { if(dp[v][j]>(ll)dp[u][j-1]+e[i].cost) { dp[v][j]=dp[u][j-1]+e[i].cost; pre[v][j]=u; } } } } } void show(int i,int j) { if(pre[i][j]==-1) { printf("1 "); return; } show(pre[i][j],j-1); printf("%d ",i); } int main() { scanf("%d%d%d",&n,&m,&T); for(int i=1;i<=m;i++) { int u,v,t; scanf("%d%d%d",&u,&v,&t); add(u,v,t); in[v]++; } for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) dp[i][j]=INF; dp[1][1]=0; pre[1][1]=-1; solve(); for(int i=n;i>=1;i--) if(dp[n][i]<=T) { printf("%d\n",i); show(n,i); break; } return 0; }