蓝桥杯-装饰珠

本文介绍了一道名为“装饰珠”的蓝桥杯竞赛题目,通过将其转化为背包问题进行解决。文章详细解释了如何利用分组背包的思想,对不同等级装饰珠进行枚举,并给出了具体的时间与空间复杂度分析及C++实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目 (蓝桥杯-装饰珠)

题目描述

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

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

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

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

输入描述

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

思路

本题是选择性问题,很显然题目的本质是一个背包问题。6个装备可以看成同一个装备,孔的等级才是关键。
我们观察两个不同的等级的珠子L和L-1。对于L而言它可以放入L ~ 4的孔中,而L-1能放入L-1 ~ 4的孔中。即L的珠子能放的孔,L-1必然可以放,我们就可以将L从大到小进行珠子的枚举,注意每次等级变化会将背包的容量也扩大。
再考虑一个种类的珠子的放入,放入只考虑0 ~ PiP_iPi,且我们只可能从中选择一个,这就是经典的分组背包问题,所以每一个种类的珠子就是一个分组,仅可选择其中的一个,空间复杂度就为O(6N)O(6N)O(6N),N为每个装备的最多孔数。
时间复杂度为O(mlog⁡m+m∗6∗N∗7)=O(mlog⁡m+2100m)O(m\log{m} +m*6*N*7 ) = O(m\log{m} + 2100m)O(mlogm+m6N7)=O(mlogm+2100m)

代码

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 305 , M = 1e4+5;
int cnt[5];
int n,m,dp[N];

struct node
{
  int l,mx;
  int w[8];
  bool operator < (const node & t)const
  {
    return  l > t.l;
  }
}p[M];

int main()
{
  for(int i = 1 ; i <= 6 ; i ++)
  {
    cin>>n;
    int x;
    for(int j = 1 ; j <= n ; j ++)
    {
      cin>>x;
      cnt[x]++;
    }
  }

  cin>>m;
  for(int i = 1 ; i <= m ; i ++)
  {
    cin>>p[i].l>>p[i].mx;
    for(int j = 1 ; j <= p[i].mx ; j ++)
      cin>>p[i].w[j];
  }
  sort(p+1,p+m+1);
   memset(dp, 0x80, sizeof dp), dp[0] = 0;
    int ans = 0, sum = 0, i = 1;
    for (int L = 4; L >= 1; L--) {
        sum += cnt[L];
        while(p[i].l == L)
        {
          for(int  k = sum ; k >= 0 ; k --)
          {
            for(int j = 1 ; j <= p[i].mx ; j ++)
            {
              if(k >= j)
                dp[k] = max(dp[k],dp[k-j]+p[i].w[j]);
            }
          }
          i++;
        }
    }
  for (int i = 0; i <= sum; i++) ans = max(ans, dp[i]);
  cout<<ans;
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值