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("");}
- Codeforces Round #436 (Div. 2) E. Fire(背包DP+输出路径)
- Codeforces Round #436 (Div. 2)-E-Fire(01背包输出路径)
- Codeforces Round #436 (Div. 2) E. Fire(01背包+输出路径)
- Codeforces 864E (Codeforces Round #436 (Div. 2)) E. Fire 背包输出路径
- Codeforces Round #436 E.Fire(01背包 + 输出路径)
- 【01背包 && 记录路径 && 约束】Codeforces Round #436 (Div. 2) E. Fire
- Codeforces Round #436 (Div. 2)-背包&排序&输出状态-E. Fire
- Codeforces Round #436 (Div. 2) E fire DP
- Codeforces Round #436 (Div. 2) E. Fire
- Codeforces Round #436 (Div. 2)E.Fire
- Codeforces Round #436 (Div. 2)E. Fire
- Codeforces Round #436 (Div. 2) E. Fire
- Codeforces Round #436 (Div. 2) E. Fire (有放入顺序有关的01背包)
- Codeforces Round #436 (Div. 2) 864E. Fire
- Codeforces Round #360 (Div. 2) E dp 类似01背包
- Codeforces Round #105 (Div. 2) E. Porcelain —— DP(背包问题)
- Codeforces Round #436 E. Fire
- codeforces 864E Fire (排序+背包dp)
- bzoj3390 [Usaco2004 Dec]Bad Cowtractors牛的报复(最大生成树)
- 剪切板(复制,粘贴)工具类s
- GKGameModelPlayer
- 交换机的堆叠与级联
- 百度地图API入门
- Codeforces Round #436 (Div. 2) E. Fire(背包DP+输出路径)
- kotlin学习笔记——操作符
- redis配置文件 redis.conf说明
- Ubuntu使用shadowsocks和chrome
- makefile终极目标
- jquery 字符串转数组
- SQL ID自增长
- 企业高效研发实践专场_加速研发效能体系升级
- 复制的项目,在Eclpise中的tomcat Add and Remove名称和项目名称不符问题