蓝桥杯真题:装饰珠

原版链接:蓝桥杯真题——装饰珠_Hey XIN的博客-CSDN博客_蓝桥杯装饰珠

题目描述

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

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

装饰珠有 MM 种,编号 1 至 M,分别对应 MM 种技能,第 i 种装饰珠的等级为 Li​,只能镶嵌在等级大于等于 L_iLi​ 的装饰孔中。

对第 ii 种技能来说,当装备相应技能的装饰珠数量达到 K_iKi​ 个时,会产生 Wi​(Ki​) 的价值。镶嵌同类技能的数量越多,产生的价值越大,即 Wi​(Ki​−1)<Wi​(Ki​)。但每个技能都有上限Pi​(1≤Pi​≤7),当装备的珠子数量超过 P_iPi​ 时,只会产生 Wi​(Pi​) 的价值。

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

输入描述

输入的第 1 至 6 行,包含 6 件装备的描述。其中第 ii 行的第一个整数 N_iNi​ 表示第 ii 件装备的装饰孔数量。后面紧接着 Ni​ 个整数,分别表示该装备上每个装饰孔的等级 L (1≤L≤4)。

第 7 行包含一个正整数 M,表示装饰珠 (技能) 种类数量。

第 8 至 M + 7 行,每行描述一种装饰珠 (技能) 的情况。每行的前两个整数Lj​ (1≤Lj​≤4) 和 Pj​ (1≤Pi​≤7) 分别表示第 jj 种装饰珠的等级和上限。接下来 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

样例说明

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

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

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

运行限制

  • 最大运行时间:5s
  • 最大运行内存: 256M

分析:

这题是个背包问题,我们把输入的数据拆成两个组:

用一个Hole[5]的vector保存对应每个等级的孔有多少个(最高等级是4,这里从下标1开始),然后是用一个L数组保存不同装饰珠的等级,W保存对应L[i]的珠子装x个的价值W[i][x]

  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);
  vector<vector<int>> W(N+1,vector<int>());
  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];
    }
  }

装饰孔相当于空间,装饰珠是我们要装的东西,这里和背包问题有不同。

第一是等级的限制:

珠子只能装到大于等于它等级的孔中,这限制了我们做的一些顺序。我们用dp[N+1][total+1]大小的向量容器来装dp的结果,N表示珠子的种类,total表示孔的总数,dp[i][j]表示的是从最高等级的往下装到i等级时,有j个孔时的最佳方案,为什么是从最高等级向最低等级呢?因为这里等级是向下兼容的,比如1的可以装到2的孔里,但是2的装不进1的孔里,所以我们反着遍历等级,就不会发生等级的冲突,容量也是依次增大:

  for(int level=4;level>=1;--level)
  {
    sum+=Hole[level];
    if(sum==0) continue;
    for(int k=1;k<=N;++k)
    {
      if(L[k]==level)
      {
        ++kind;
        for(int i=1;i<=sum;++i)
        {
          dp[kind][i]=dp[kind-1][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 <iostream>
#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);
  vector<vector<int>> W(N+1,vector<int>());
  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];
    }
  }
  int sum=0,kind=0;
  vector<vector<int>> dp(N+1,vector<int>(total+1));
  for(int level=4;level>=1;--level)
  {
    sum+=Hole[level];
    if(sum==0) continue;
    for(int k=1;k<=N;++k)
    {
      if(L[k]==level)
      {
        ++kind;
        for(int i=1;i<=sum;++i)
        {
          dp[kind][i]=dp[kind-1][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());
  return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值