TZOJ 7648: 维克托的出装 背包路径
描述
CJ最近用维克托猛猛冲分上了钻石,CJ发现每一局根据对手的角色不同系统会给他推荐不同的装备,这很好的让CJ减少了装备选择上的烦恼,但是CJ的发育能力并不是很好,他的金币并不多只有m,现在系统给他推荐了n件装备,每件装备都能提高伤害,但是也需要CJ花金币购买,现在CJ想请你帮他计算一下要如何购买装备才能让他的维克托打出最高的伤害。
输入
输入第一行为n,m(1≤n≤50,1≤m≤3000)
接下来n行每行三个内容ai,wi,vi,代表第i件装备的名称、需要的金币、能造成的伤害。
(ai为长度不超过20的字符串,只包含小写英文字母,1≤wi≤400,1≤vi≤150)
输出
第一行请输出CJ的维克托能打出的最高伤害
第二行输出应该购买的装备,按序号由小到大输出装备名称
样例输入
4 1000
ldhs 320 80
wyzh 300 75
swzm 360 120
yy 300 100
样例输出
300
ldhs swzm yy
题意:经典01背包问题,第i个物品的j容量时最大价值的状态转移方程为f[i][j] = max(f[i-1][j],f[i-1][j-w[i]]+v[i]);当然如果是容量j已经装不下第i个物品的重量w[i],那么我们就应该让第i个物品的j容量的价值f[i][j]继承上一个物品i-1的价值也就是f[i][j] = f[i-1][j];
最后从第n个物品开始倒序,如果当前状态和上一个状态i-1不想等,证明第i个物品是选取的 ,那么就给第i个物品标记上,并且记录选取的物品数k++,并让容量m减去这个选取的物品重量w[i],就可以得到上一个容量的状态了,然后继续判断接下来的物品在这个容量下的价值是否和上一个物品的这个容量的价值是不一样的,继续选取就行。
代码
#include<bits/stdc++.h> using namespace std; char name[51][21]; int n,m; int f[51][3001]; int book[51],w[51],v[51]; int main() { cin>>n>>m; for(int i=1;i<=n;i++) { cin>>name[i]>>w[i]>>v[i]; } for(int i=1;i<=n;i++) { for(int j=m;j>=0;j--) { if(j-w[i]>=0) f[i][j] = max(f[i-1][j],f[i-1][j-w[i]]+v[i]); else//如果不选这个物品,就继承上一个状态i-1的值 f[i][j] = f[i-1][j]; } } cout<<f[n][m]<<endl; int k=0,flag=0; if(f[n][m]>0)flag = 1; for(int i=n;i>=1;i--) { if(f[i][m]!=f[i-1][m])//如果当前状态和上一个状态i-1不想等,证明第i个物品是选取的 { book[i] = 1;//标记 m-=w[i];//减去第i个物品的重量,就可以得到上一个物品的状态 k++; } } if(flag){ int s = 0; for(int i=1;i<=n;i++) { if(book[i]==1) { cout<<name[i]; s++; if(s!=k)cout<<" "; } } } return 0; }