装饰珠
在怪物猎人这一款游戏中,玩家可以通过给装备镶嵌不同的装饰珠来获取 相应的技能,以提升自己的战斗能力。
已知猎人身上一共有 6 件装备,每件装备可能有若干个装饰孔,每个装饰孔有各自的等级,可以镶嵌一颗小于等于自身等级的装饰珠 也可以选择不镶嵌。
装饰珠有 M 种,编号 1 至 M,分别对应 M 种技能,第 i 种装饰珠的等级为 \(Li\),只能镶嵌在等级大于等于 \(Li\) 的装饰孔中。
对第 i 种技能来说,当装备相应技能的装饰珠数量达到 \(Ki\)个时,会产生\(WiKi\)的价值,镶嵌同类技能的数量越多,产生的价值越大,即\(WiKi−1
<WiKi\)
- 但每个技能都有上限\(Pi1≤Pi≤7\)
- 当装备的珠子数量超过Pi时,只会产生\(WiPi\)的价值。
对于给定的装备和装饰珠数据,求解如何镶嵌装饰珠,使得 6 件装备能得到的总价值达到最大。
来源: 圆宝の博客
文章作者: 郭梦圆
文章链接: https://ybao.xyz/2021/04/22/蓝桥杯真题——装饰珠/
本文章著作权归作者所有,任何形式的转载都请注明出处。
思路
这道题的题面较为复杂,但理清以后会发现,装备数其实没什么用, 实质是把各种各样的珠子放进不同等级的孔里(珠子等级≤孔等级)。
- 同时每种珠子放进若干个会产生一个递增的价值,用一个数组描述;放多少个有上限,比如上限为5,则放进6个和5个的价值相同。
这道题和背包问题很像,但有两点不同
-
第一点是高等级的珠子不能放进低等级的孔中,放入有限制;
因此选择从高等级到低等级逐层开放孔数,同时优先放同等级的珠子,放入时就不会受到限制 -
第二点是同种珠子放入不同的个数的价值是用一个数组描述的,而不是每个珠子是一样的价值。
因此还需要遍历一下放入不同个数的情况。
这样即可转换为背包问题,孔数即相当于容量。
以\(dp[i][j]\)表示前 \(i\) 种珠子放入 \(j\) 个孔中所能产生的最大价值。(这里前 i 个不是按照原来顺序的)
每开放一个等级就枚举与之等级相同的珠子放多少个,然后与前\(i - 1\)种珠子相对应转移过来的情况进行比较。
//必须先平行转移,因为有好几种转移情况(放i个)
for (int k = 0; k <= len; k++)
f[tot][k] = f[tot - 1][k];
//放i个该种珠子
for (int k = 1; k <= p[j]; k++)
for (int o = k ; o <= len; o++)
f[tot][o] = max(f[tot][o], f[tot - 1][o - k] + vec2[j][k - 1]);
代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e4 + 10;
int n;
int x[5];
int l[N], p[N];
vector<int> vec2[N];
int f[N][305];
signed main()
{
int n = 0;//总孔数
for (int i = 0; i < 6; i++){
int k;cin >> k;
n += k;
for (int j = 0; j < k; j++){
int t;cin >> t;
x[t]++;
}
}
int m;
cin >> m;
for (int i = 1; i <= m; i++){
cin >> l[i] >> p[i];
for (int j = 0; j < p[i]; j++){
int t;
cin >> t;
vec2[i].push_back(t);
}
}
int len = 0, tot = 0;
for (int i = 4; i >= 1; i--){
len += x[i];// 逐步开放
for (int j = 1; j <= m; j++){
if (l[j] != i) continue;
tot++;
//必须先平行转移,因为有好几种转移情况(放i个)
for (int k = 0; k <= len; k++)
f[tot][k] = f[tot - 1][k];
//放i个该种珠子
for (int k = 1; k <= p[j]; k++)
for (int o = k ; o <= len; o++)
f[tot][o] = max(f[tot][o], f[tot - 1][o - k] + vec2[j][k - 1]);
}
}
int ans = 0;
for (int i = 0; i <= len; i++) ans = max(ans, f[tot][i]);
cout << ans << endl;
return 0;
}