装饰珠

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

已知猎人身上一共有 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;
}
posted @ 2023-03-20 22:08  kingwzun  阅读(47)  评论(0编辑  收藏  举报