分组背包
- 分组问题在实现的时候就想分层一样,每一种商品就是一层
- 这一层基于上一层计算
题意:
13种装备(每种可能会有多件),每件装备有两个属性:伤害,韧性。现在一个人想装备这些装备,目标是达到韧性m,使得伤害最高,输出最高伤害。如果达不到目标韧性则输出-1.
有两个条件:
- 如果挑选了“Two-Handed”就不能挑选“Weapon”和“Shield”,如果不挑选则这两件都可以挑选。
- “Finger”可以选择两件
思路:
根据题意,每种装备只能装备一件,有点像0-1背包。0-1背包中容量应该是m(韧性),但是这道题种要求的是需要一定大于等于m的韧性,不像0-1背包中的容量随意可以拆分。
所以:第i种装备是否可以挑选是建立在第i-1装备的基础上的。根据这种思想写出状态表达式。
dp[i][j+t]=max(dp[i][j+t],dp[i−1][j]+d): 表示装备第i种装备会增加t的韧性,和上一件装备的状态加上伤害d取最大值即可。然后只需要遍历第i种装备的每一件即可,其呈现的效果就像bfs一样成为一层的装备状态。
还有两个条件限制:
对于“Two-Handed” 把它和“Weapon”与“Shield”当做一种商品,并且“Weapon”与“Shield” 的组合也放在一起。这样挑选就不会起冲突了。
类似的:“Finger” 和其组合放在一起当做一种商品,就会利用每一种商品计算的时候分层而不会冲突。
- 注意:即使装备种类没有13种,也要保存上一种的情况。也就是
dp[i][j]的初始化。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <map>
#include <vector>
using namespace std;
struct goods
{
int Damage,Toughness;
};
map<string,int>mp;
vector<goods>G[15];
int n,m;
int dp[15][50005];
void init()
{
mp["Weapon"] = 1;
mp["Shield"] = 2;
mp["Two-Handed"] = 3;
mp["Finger"] = 4;
mp["Feet"] = 5;
mp["Legs"] = 6;
mp["Waist"] = 7;
mp["Wrist"] = 8;
mp["Hand"] = 9;
mp["Torso"] = 10;
mp["Neck"] = 11;
mp["Shoulder"] = 12;
mp["Head"] = 13;
}
int main()
{
//freopen("in.txt","r",stdin);
init();
int tt;
cin>>tt;
while(tt--) {
for(int i = 1;i <= 13; i++)
G[i].clear();
cin>>n>>m;
for(int i = 1;i <= n; i++) {
string s;
int d,t;
cin>>s>>d>>t;
G[mp[s]].push_back((goods){d,t});
}
int length1 = G[1].size();
int length2 = G[2].size();
for(int j = 0;j < length2; j++) {
G[3].push_back(G[2][j]);
}
for(int i = 0;i < length1; i++) {
G[3].push_back(G[1][i]);
for(int j = 0;j < length2; j++) {
G[3].push_back((goods){G[1][i].Damage+G[2][j].Damage,G[1][i].Toughness+G[2][j].Toughness});
}
}
int length3 = G[4].size();
for(int i = 0;i < length3; i++) {
for(int j = i + 1;j < length3; j++) {
G[4].push_back((goods){G[4][i].Damage+G[4][j].Damage,G[4][i].Toughness+G[4][j].Toughness});
}
}
memset(dp,-1,sizeof(dp));
dp[2][0] = 0;
for(int i = 3;i <= 13; i++) {
for(int j = 0;j <= m; j++) {
dp[i][j] = max(dp[i][j],dp[i-1][j]); //更新装备状态,即使后边没有装备了,也能到到之前的状态。(必须步骤)
if(dp[i-1][j] == - 1) continue; //若要买下一个装备,那么上一个装备的状态一定要达到
int length = G[i].size();
for(int k = 0;k < length; k++) {
goods e = G[i][k];
int d = min(e.Toughness + j,m); //如果大于m则当做m即可(极妙)
dp[i][d] = max(dp[i][d],dp[i-1][j]+e.Damage);
}
}
}
cout<<dp[13][m]<<endl;
}
return 0;
}