1. 程式人生 > >ZOJ 3769 (分組揹包)

ZOJ 3769 (分組揹包)

分組揹包

  • 分組問題在實現的時候就想分層一樣,每一種商品就是一層
  • 這一層基於上一層計算

題意:

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[i1][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; }