人物装备的选取 - 分组背包

Diablo III is an action role-playing video game. A few days ago, Reaper of Souls (ROS), the new expansion of Diablo III, has been released! On hearing the news, the crazy video game nerd Yuzhi shouted: "I'm so excited! I'm so excited! I wanna kill the Diablo once more!"

The ROS introduced a lot of new features and changes. For example, there are two new attributes for players in the game: Damage and Toughness. The attribute Damage indicates the amount of damage per second you can deal and the Toughness is the total amount of raw damage you can take.

To beat the Diablo, Yuzhi need to select the most suitable equipments for himself. A player can carry at most 13 equipments in 13 slots: Head, Shoulder, Neck, Torso, Hand, Wrist, Waist, Legs, Feet, Shield, Weapon and 2 Fingers. By the way, there is a special type of equipment: Two-Handed. A Two-Handed equipment will occupy both Weapon and Shield slots.

Each equipment has different properties on Damage and Toughness, such as a glove labeled "30 20" means that it can increase 30 Damage and 20 Toughness for the player who equips it in the Hand slot. The total Damage and Toughness is the sum of Damage and Toughness of all equipments on the body. A player without any equipments has 0 Damage and 0 Toughness.

Yuzhi has N equipments stored in his stash. To fight against the Diablo without lose the battle, he must have at least M Toughness. In addition, he want to finish the battle as soon as possible. That means the Damage should be as much as possible. Please help Yuzhi to determine which equipments he should take.


Input

There are multiple test cases. The first line of input is an integer T indicates the number of test cases. For each test case:

The first line contains 2 integers N (1 <= N <= 300) and M (0 <= M <= 50000). The next N lines are the description of equipments. The i-th line contains a string Si and two integers Di and Ti (1 <= Di, Ti <= 50000). Si is the type of equipment in {"Head", "Shoulder", "Neck", "Torso", "Hand", "Wrist", "Waist", "Legs", "Feet", "Finger", "Shield", "Weapon", "Two-Handed"}. Di and Ti are the Damage and Toughness of this equipment.

Output

For each test case, output the maximum Damage that Yuzhi can get, or -1 if he can not reach the required Toughness.

Sample Input
2
1 25
Hand 30 20
5 25
Weapon 15 5
Shield 5 15
Two-Handed 25 5
Finger 5 10
Finger 5 10
Sample Output
-1
35


题意:一个人有 13 个空槽可以用来放置装备,并且对一些装备有一些要求,求在耐久度超过 m 时可以造成的最大的伤害。
思路分析:
  首先定义状态, dp[i][j]枚举第 i 件装备时,此时所选取装备的耐久度为 j 的最大伤害值。
  这个题有个不同的地方在于其要求耐久度是大于等于 m 即可,而不是指定给你一个背包的容量, 那么这个转移方程就要区别于我们以往所写的 减 的形式的转移方程,
这里其最终有意义的是耐久度大于 m 的时候,因此对于大于 m 的耐久度的状态我们都可以当成 m 来看待,可以写出转移方程 dp[i][j+t]=max(dp[i][j+t], dp[i-1][j]+d)
  这里有一个很重要的优化,就是要对每组背包的数量进行一个从大到小的排序。因为他们对于选取的顺序是没有要求的,如果前面先选取状态种类多的,只需要访问少有的几次,后面再选取状态种类多的,则需要对于每一个上面继承来的状态,当前状态都要再去访问一遍,直接会导致超时。

 代码示例:
int n, m;
map<string, int>mp;

void init(){
    mp["Head"] = 1;
    mp["Shoulder"] = 2;
    mp["Neck"] = 3;
    mp["Torso"] = 4;
    mp["Hand"] = 5;编辑
    mp["Wrist"] = 6;
    mp["Waist"] = 7;
    mp["Legs"] = 8;
    mp["Feet"] = 9;
    mp["Finger"] = 10;
    mp["Two-Handed"] = 11;
     
    mp["Shield"] = 12;
    mp["Weapon"] = 13;
}
struct node
{
    int d, t;
    
    node(int _d=0, int _t=0):d(_d), t(_t){}
};
vector<node>ve[20], pp;

void init2(){
    pp.clear();
    for(int i = 0; i < ve[10].size(); i++){
        for(int j = i+1; j < ve[10].size(); j++){
            int x = ve[10][i].d+ve[10][j].d;
            int y = ve[10][i].t+ve[10][j].t;
            pp.push_back(node(x, y));
        }
    }
    for(int i = 0; i < pp.size(); i++) ve[10].push_back(pp[i]);
    
    for(int i = 0; i < ve[12].size(); i++){
        for(int j = 0; j < ve[13].size(); j++){
            int x = ve[12][i].d+ve[13][j].d;
            int y = ve[12][i].t+ve[13][j].t;
            ve[11].push_back(node(x, y));
        }
    } 
}
int dp[15][50005];
int cmp(vector<node> x,vector<node> y){  
    return x.size()>y.size();  
}  

int main() {
    //freopen("in.txt", "r", stdin);
    //freopen("out.txt", "w", stdout);
    int t;
    init();
    char s[50];
    int a, b; // 伤害 耐久度
    
    cin >> t;
    while(t--){
        scanf("%d%d", &n, &m);
        for(int i = 1; i <= 15; i++) ve[i].clear();
        for(int i = 1; i <= n; i++){
            scanf("%s%d%d", s, &a, &b);
            ve[mp[s]].push_back(node(a, b)); 
            if (mp[s] == 12 || mp[s] == 13) ve[11].push_back(node(a, b));
        }
        init2();
        //printf("************\n");
        memset(dp, -1, sizeof(dp));        
        dp[0][0] = 0;
        //for(int i = 0; i <= 11; i++) dp[0][i] = 0;
          sort(ve+1,ve+12,cmp);   
        //printf("----------\n");
        for(int i = 1; i <= 11; i++){
            for(int j = m; j >= 0; j--){ // 耐久度
                dp[i][j] = dp[i-1][j];
                if (dp[i-1][j] == -1) continue;
                for(int k = 0; k < ve[i].size(); k++){
                    a = ve[i][k].d, b = ve[i][k].t;
                    int tem = min(m, j+b);
                    dp[i][tem] = max(dp[i][tem], dp[i-1][j]+a);
                //    printf("+++ %d  %d %d \n", i, tem, dp[i][tem]);
                }
                //printf("+++ %d %d %d \n", i, j, dp[i][j]);
            }
        }
        printf("%d\n", dp[11][m]);
    }
    return 0;
}

 

posted @ 2018-06-04 23:48  楼主好菜啊  阅读(265)  评论(0编辑  收藏  举报