Diablo III ZOJ - 3769(分组背包)

Diablo III

题目链接: ZOJ - 3769
题意:小明要买一套装备去刷图,每个整套由{"Head"(头盔), "Shoulder"(肩甲), "Neck"(护颈), "Torso"(护甲), "Hand"(手套), "Wrist"(护腕), "Waist"(腰甲), "Legs"(绑腿), "Feet"(靴子), "Finger"(护指), "Shield"(护盾), "Weapon"(武器——大宝剑)}十二部分组成, 每部分的装备最多只能一件("Finger"(护指)可以装备两件), 还有一种装备——"Two-Handed"(攻防全能型装备), 一件此装备可以顶替 "Shield"(护盾),和"Weapon"(武器——大宝剑).,每件装备有两个属性——Damage(伤害) and Toughness(韧性)
由于不差钱, 小明要求装备的伤害附加值之和越高越好, 同时装备的韧性之和大于等于m;
求出最大伤害值;
比较麻烦的是Finger和Two-Handed,  Shield , Weapon;Finger可以装备一件, 也可以装备两件, 而Two-Handed可以顶替  Shield 和 Weapon, 这两种状态难控制;
所以我们可以将Finger两两配对来表示一件,
同样的Shield和Weapon也可以用Two-Handed表示, Shield和Weapon相互匹配, 相当于一件Two-Handed;
dp[i][j]  i表示第类件装备, j表示此时的Toughness
由于最后求的是Toughness大于m时的最大Damage, 所以当Toughness大于m时, 结果就存到m中;
状态转移方程:
        dp[i][j+t]=max(dp[i][j+t], d[i+1][j]+d);
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
string equip[]={"Head", "Shoulder", "Neck", "Torso", "Hand", "Wrist", "Waist",
                "Legs", "Feet", "Finger", "Shield", "Weapon", "Two-Handed"};
struct node{
    int d, t;
};
vector<node> vec[15];
int get_id(string s){
    for(int i=0; i<13; i++){
        if(s==equip[i]) return i;
    }
    return -1;
}
int dp[15][50010];
int main(){
    int T;
    scanf("%d", &T);
    while(T--){
        int n, m;
        string s;
        for(int i=0; i<13; i++) vec[i].clear();
        node tmp;
        scanf("%d%d", &n, &m);
        for(int i=0; i<n; i++){
            cin >> s >> tmp.d >> tmp.t;
            int z=get_id(s);
            vec[z].push_back(tmp);
            if(z==10||z==11) vec[12].push_back(tmp);//如果是"Shield"或"Weapon", 存到"Two-Handed"中;
        }

        //将"Shield"和"Weapon"两两结合当作"Two-Handed";
        for(int i=0; i<vec[10].size(); i++){
            for(int j=0; j<vec[11].size(); j++){
                tmp.d=vec[10][i].d+vec[11][j].d;
                tmp.t=vec[10][i].t+vec[11][j].t;
                vec[12].push_back(tmp);
            }
        }
        //将Finger两两结合;
        vec[10].clear();//"Shield"清空, 用来存Finger;
        for(int i=0; i<vec[9].size(); i++){
            vec[10].push_back(vec[9][i]);
            for(int j=i+1; j<vec[9].size(); j++){
                tmp.d=vec[9][i].d+vec[9][j].d;
                tmp.t=vec[9][i].t+vec[9][j].t;
                vec[10].push_back(tmp);
            }
        }
        vec[9].clear();//Finger已经转移到vec[10]中, 此处清空;

        memset(dp, -1, sizeof(dp));//-1表示无此状态;
        int vt, vd;
        dp[11][0]=0;
        //"Two-Handed"单独处理;
        for(int i=0; i<vec[12].size(); i++){
            tmp=vec[12][i];
            vt=tmp.t>=m?m:tmp.t;
            vd=tmp.d;
            dp[11][vt]=max(dp[11][vt], vd);
        }
        for(int i=10; i>=0; 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;//i+1没有j这个状态;
                for(int k=0; k<vec[i].size(); k++){
                    tmp=vec[i][k];
                    vt=(tmp.t+j)>=m?m:(tmp.t+j);
                    vd=tmp.d;
                    dp[i][vt]=max(dp[i][vt], dp[i+1][j]+vd);
                }
            }
        }
        printf("%d\n", dp[0][m]);
    }
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值