Codeforces Round #436 (Div. 2) E. Fire(背包DP+输出路径)

题意:

现在着火了,你要保留物品,每个物品有三个值t,d,p,t是解救需要的时间,d是过了多少秒这个物品就烧没了,p是物品价值,问保留物品最大的价值是多少,有多少个,分别是哪几个?

思路:

很明显的DP,而且总是觉得在哪里见过。dp[i]表示到时间为i为止,所能取得的最大价值,转移方程:dp[j]=max(dp[j],dp[j-a[i].t]+a[i].p),有点类似于背包,只不过这里放的是时间。还有一点,如果把物品按照销毁时间进行排序,就不用担心时间出现冲突了,这样当前最优状态就可以由以前状态推导出来。这样就能求出最大价值。
然后,要求输出路径,path[i][j]表示对于按照时间顺序,取到第i个物品且时间是j时候的最优取法的情况,如果path[i][j]是i,则代表它是路径的末端(其实是第一个取走的),否则就是取得该情况的上一个的节点(-1代表不存在)。这样,我们首先遍历出最大价值,就能一步步找回路径了
(因为之前不会保存路径,也就在很多细节上想了许多,都写在注释里面了)

更新

上面的写法简直反人类,其实能够有更简单的写法,path[i][j]表示按照销毁时间顺序第i个物品,时间为j时是否能实现,这样我记录下最大时候的下标,就可以逆推了,这样的写法很好理解,也不容易错,就很优秀
我之前path[i][j]记录的是按照销毁时间顺序第i个物品,价值为j时是否能实现,emmmm,虽然能输出一个路径,但是很容易能想到会出现时间冲突。。。只能说
差之毫厘,谬以千里啊。。。

错误及反思:

一群 WA。主要是不会保存路径,只能现学,path的用法一开始想复杂了,导致出现了各种思路上的问题,最后还是找了个代码学习了一下才A的。。。

代码:

#include<bits/stdc++.h>
using namespace std;
struct poi{
    int num,t,d,p;
    operator < (const poi & x){
        return d<x.d;
    }
}a[110];
int dp[2100];
int ans[110];
int path[110][2100];
int n;
int main(){
    scanf("%d",&n);
    for(int i=0;i<n;i++)
    {
        scanf("%d%d%d",&a[i].t,&a[i].d,&a[i].p);
        a[i].num=i+1;
    }
    sort(a,a+n);
    int maxn=0,times=0,tmp=0;
    memset(path,-1,sizeof(path));
    for(int i=0;i<n;i++)
    {
        if(i){
            for(int j=1;j<a[i].t;j++)
                path[i][j]=path[i-1][j];
        }//在a[i].t之前的必定不会改变
        for(int j=a[i].d-1;j>=a[i].t;j--)
        {
            if(dp[j-a[i].t]+a[i].p>dp[j]){
                dp[j]=dp[j-a[i].t]+a[i].p;
                path[i][j]=i;
                if(dp[j]>=maxn)//这里的等于号很关键,因为如果不是第一个出现的时间
                {              //那么在推导路径的时候,就会有可能重复选择
                    maxn=dp[j];//比如:对于1物品来说,1-10分钟的最优选择都是1物品(也就是说1物品花费1分钟)
                    tmp=j;     //而如果对于2物品来说,如果1-10分钟的最优还是1物品
                }              //那么我们在选择的时候,如果是以9分钟的条件返回的(就是说我们跳过了2物品)
            }                  //我们就还会再选择一次1物品
            else if(i)//没改变就继承下来
                path[i][j]=path[i-1][j];
        }
        //以外的区间没有对path操作是因为我们不可能选择该物品了,也就没有必要了
    }
    int tmp1=n-1;
    while(tmp>0)
    {
        ans[times++]=a[path[tmp1][tmp]].num;
        tmp-=a[path[tmp1][tmp]].t;
        tmp1--;//向前找物品
        tmp1=path[tmp1][tmp];
    }
    printf("%d\n%d\n",maxn,times);
    for(int i=times-1;i>=0;i--)
        printf("%d ",ans[i]);
    puts("");
}

更新后更好理解的思路的代码:

#include<bits/stdc++.h>
using namespace std;
struct poi{
    int num,t,d,p;
}a[110];
int dp[2010];
int path[110][2010];
int ans[110];
int n;
bool cmp(poi x,poi y){
    return x.d<y.d;
}
int main(){
    scanf("%d",&n);
    for(int i=0;i<n;i++)
    {
        scanf("%d%d%d",&a[i].t,&a[i].d,&a[i].p);
        a[i].num=i+1;
    }
    sort(a,a+n,cmp);
    int maxn=0;
    int times=0;

    for(int i=0;i<n;i++)
    {
        for(int j=a[i].d-1;j>=a[i].t;j--)
        {
            if(dp[j-a[i].t]+a[i].p>dp[j]){
                dp[j]=dp[j-a[i].t]+a[i].p;
                path[i][j]=1;
                if(dp[j]>dp[maxn])
                    maxn=j;
            }
        }
    }
    printf("%d\n",dp[maxn]);
    int cnt=n-1;
    while(cnt>=0)
    {
        if(path[cnt][maxn]==1)
        {
            ans[times++]=a[cnt].num;
            maxn-=a[cnt].t;
        }
        cnt--;
    }

    printf("%d\n",times);
    for(int i=times-1;i>=0;i--)
        printf("%d ",ans[i]);
    puts("");


}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值