Codeforces 721C. Journey(DAG上的DP)

传送门 http://codeforces.com/problemset/problem/721/C

题目大意:
有一个dag图,代表一个城市。城市里有若干景点,以及连接景点的单向道路,每条道路都有一个通过的时间ti。
你从景点1出发,到景点n,在有限的时间T内,至多能游览几个景点?

题目分析:
一开始想的是类似于背包的思路,即令 dp[i][t] 代表从1点走到i点,不超过时间t,能游览多少个景点,然后最终答案是 dp[n][T] .但题目中T的范围高达 109 ,所以考虑从前向后递推。
dp[i][j] 代表从i点走到n点,途径j个点的最小时间,那么要想从i点走到n点,必须经过i点的后继结点,且要选取局部最小花销的点去走,所以很显然满足DP的性质。
因此 dp[i][j]=min{dp[k][j1]+dist[i][k]},ki
而题目中要求输出游览的路径,所以每次更新dp[i][j],还要维护一个to[i][j],代表从i点经j步走到n点的路线中,下一个要走的点。
一开始这个数组起的名字是next[i][j],但是一直抛compilation error,百思不得其解。结果是跟头文件某处出现的next指针重名了。。。。。

这里有一个坑,就是记录距离不可使用邻接矩阵,否则会爆内存。

第一次遇到图上的DP,需要谨记此种思路,其实跟树形DP差不多,就是要加visited标记。DP的水还是深啊,微软第二题那道DP就死活想错了。。。。。。 传说,acm中一半的题都跟DP有关系……?

#include <bits/stdc++.h>
using namespace std;
vector<int> g[5005];
vector<int> dist[5005];
short to[5005][5005];
int dp[5005][5005];
bool vis[5005];
int n,m,t;
void dfs(int c) { // depth-first search from node c
    vis[c]=true;
    if(c==n)
        return;
    for(int i=0;i<g[c].size();i++) {
        int k=g[c][i],d=dist[c][i];
        if(!vis[k])
            dfs(k);
        for(int j=2;j<=n;j++) {
            if(dp[k][j-1]+d<dp[c][j]) {
                dp[c][j]=dp[k][j-1]+d;
                to[c][j]=k;
            }
        }
    }
}
int main() {
    memset(vis,0,sizeof(vis));
    memset(dist,0,sizeof(dist));
    memset(to,0,sizeof(to));
    memset(dp,0x3f,sizeof(dp));

    scanf("%d %d %d",&n,&m,&t);
    int u,v,d;
    for(int i=0;i<m;i++) {
        scanf("%d %d %d",&u,&v,&d);
        g[u].push_back(v);
        dist[u].push_back(d);
    }
    dp[n][1]=0;
    dfs(1);

    int a=0;
    for(int i=n;i>=1;i--) {
        if(dp[1][i] <= t) {
            printf("%d\n", i);
            printf("1 ");
            a=i;
            break;
        }
    }
    int j=1;
    while(a>1) {
        if(a!=1)
            printf("%d ", to[j][a]);
        else
            printf("%d", to[j][a]);
        j=to[j][a--];
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值