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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值