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

来源:互联网 发布:ubuntu怎样新建文件夹 编辑:程序博客网 时间:2024/06/10 04:01

题意:

现在着火了,你要保留物品,每个物品有三个值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 0