蓝桥杯真题——装饰珠

题目描述

在怪物猎人这一款游戏中,玩家可以通过给装备镶嵌不同的装饰珠来获取 相应的技能,以提升自己的战斗能力。

已知猎人身上一共有 6 件装备,每件装备可能有若干个装饰孔,每个装饰孔有各自的等级,可以镶嵌一颗小于等于自身等级的装饰珠 (也可以选择不镶嵌)。

装饰珠有 M 种,编号 1 至 M,分别对应 M 种技能,第 i 种装饰珠的等级为 Li,只能镶嵌在等级大于等于 Li 的装饰孔中。
对第 i 种技能来说,当装备相应技能的装饰珠数量达到 Ki个时,会产生Wi(Ki)的价值,镶嵌同类技能的数量越多,产生的价值越大,即Wi(Ki-1)<Wi(Ki)。但每个技能都有上限Pi(1≤Pi≤7),当装备的珠子数量超过Pi时,只会产生Wi(Pi)的价值。

对于给定的装备和装饰珠数据,求解如何镶嵌装饰珠,使得 6 件装备能得到的总价值达到最大。

输入描述

输入的第 1 至 6 行,包含 6 件装备的描述。其中第i行的第一个整数Ni表示第i件装备的装饰孔数量。后面紧接着Ni个整数,分别表示该装备上每个装饰孔的等级L(1≤ L ≤4)。
第 7 行包含一个正整数 M,表示装饰珠 (技能) 种类数量。
第 8 至 M + 7 行,每行描述一种装饰珠 (技能) 的情况。每行的前两个整数Lj(1≤ Lj ≤4)和Pj(1≤ Pj ≤7)分别表示第 j 种装饰珠的等级和上限。接下来Pj个整数,其中第 k 个数表示装备该中装饰珠数量为 k 时的价值Wj(k)。
其中1 ≤ Ni ≤ 50,1 ≤ M ≤ 104,1 ≤ Wj(k) ≤ 104

输出描述

输出一行包含一个整数,表示能够得到的最大价值。
输入

1 1
2 1 2
1 1
2 2 2
1 1
1 3
3
1 5 1 2 3 5 8
2 4 2 4 8 15
3 2 5 10

输出

20

样例说明
按照如下方式镶嵌珠子得到最大价值 20,括号内表示镶嵌的装饰珠的种类编号:

1: (1)
2: (1) (2)
3: (1)
4: (2) (2)
5: (1)
6: (2)

4 颗技能 1 装饰珠,4 颗技能 2 装饰珠 W1(4) + W2(4) = 5 + 15 = 20。W1(4)+W2(4)=5+15=20。

解析

这道题的题面较为复杂,但理清以后会发现,装备数其实没什么用,实质是把各种各样的珠子放进不同等级的孔里(珠子等级≤孔等级),同时每种珠子放进若干个会产生一个递增的价值,用一个数组描述,但放多少个有上限,比如上限为5,则放进6个和5个的价值相同。

这道题和背包问题很像,但有两点不同,第一点是高等级的珠子不能放进低等级的孔中,因此放入有限制;第二点是同种珠子放入不同的个数的价值是用一个数组描述的,而不是每个珠子是一样的价值。

由于一共只有4个等级,因此对于输入量,我们选择按照等级记录每个等级的孔数,还有每种珠子放若干个的价值。因为孔的等级对于珠子的等级是向下兼容的,因此选择从高等级到低等级逐层开放孔数,同时优先放同等级的珠子,放入时就不会受到限制,即可转换为背包问题,孔数即相当于容量。

以dp[i][j]表示前 i 种珠子放入 j 个孔中所能产生的最大价值,每开放一个等级就枚举与之等级相同的珠子放多少个,然后与前i - 1种珠子相对应转移过来的情况进行比较。

//必须先平行转移,因为有好几种转移情况(放i个) 
for (int i = 1; i <= sum; ++i) { 
	dp[kind][i] = dp[kind - 1][i];
}
//放i个该种珠子
for (int i = 1; i < W[k].size(); ++i) {
	for (int j = sum; j >= i; --j) {
		dp[kind][j] = max(dp[kind][j], dp[kind - 1][j - i] + W[k][i]);
	}
}

完整代码

#include <bits/stdc++.h>
using namespace std;
int main()
{
    vector<int> Hole(5); //每个等级的孔有多少
    int N, tmp, total = 0;
    for (int i = 0; i < 6; ++i) {
        cin >> N; 
        for (int j = 0; j < N; ++j) {
            cin >> tmp; 
            ++Hole[tmp];
            ++total; //总孔数
        }
    }
    cin >> N; //装饰珠种类
    vector<int> L(N + 1); //第i种的等级
    vector<vector<int>> W(N + 1, vector<int>()); //第i种放j个的价值
    for (int i = 1; i <= N; ++i) {
        cin >> L[i];
        cin >> tmp;
        W[i].resize(tmp + 1);
        for (int j = 1; j <= tmp; ++j) {
            cin >> W[i][j];
        }
    }
    vector<vector<int>> dp(N + 1, vector<int> (total + 1));
    int sum = 0;
    int kind = 0;
    for (int level = 4; level >= 1; --level) {
        sum += Hole[level]; // 该等级开放的孔数
        if (sum == 0)   continue;
        for (int k = 1; k <= N; ++k) { //第k种珠子
            if (L[k] == level) {
                ++kind;
                //必须先平行转移,因为有好几种转移情况(放i个) 
                for (int i = 1; i <= sum; ++i) {
                    dp[kind][i] = dp[kind - 1][i];
                }
            	//放i个该种珠子
                for (int i = 1; i < W[k].size(); ++i) {
                    for (int j = sum; j >= i; --j) {
                        dp[kind][j] = max(dp[kind][j], dp[kind - 1][j - i] + W[k][i]);
                    }
                }
            }
        }
    }
    cout << *max_element(dp[kind].begin(), dp[kind].end()) << endl;
    return 0;
}


  • 14
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 13
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值