题目 (蓝桥杯-装饰珠)
题目描述
在怪物猎人这一款游戏中,玩家可以通过给装备镶嵌不同的装饰珠来获取 相应的技能,以提升自己的战斗能力。
已知猎人身上一共有 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(Ki−1)<Wi(Ki)。但每个技能都有上限Pi(1≤Pi≤7)P_i(1≤P_i≤7)Pi(1≤Pi≤7),当装备的珠子数量超过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(1≤Lj≤4)和Pj(1≤Pj≤7)P_j(1≤ P_j ≤7)Pj(1≤Pj≤7)分别表示第 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^41≤Ni≤50,1≤M≤104,1≤Wj(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(mlogm+m∗6∗N∗7)=O(mlogm+2100m)O(m\log{m} +m*6*N*7 ) = O(m\log{m} + 2100m)O(mlogm+m∗6∗N∗7)=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;
}