ZOJ 3769 (分組揹包)
阿新 • • 發佈:2019-01-25
分組揹包
- 分組問題在實現的時候就想分層一樣,每一種商品就是一層
- 這一層基於上一層計算
題意:
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;
}