目录
题目描述
在怪物猎人这一款游戏中,玩家可以通过给装备镶嵌不同的装饰珠来获取相应的技能,以提升自己的战斗能力。
已知猎人身上一共有 6 件装备,每件装备可能有若干个装饰孔,每个装饰孔有各自的等级,可以镶嵌一颗小于等于自身等级的装饰珠 (也可以选择不镶嵌)。
装饰珠有 种,编号
至
,分别对应
种技能,第
种装饰珠的等级为
,只能镶嵌在等级大于等于
的装饰孔中。
对第 种技能来说,当装备相应技能的装饰珠数量达到
个时,会产生
的价值。镶嵌同类技能的数量越多,产生的价值越大,即
。但每个技能都有上限
,当装备的珠子数量超过
时,只会产生
的价值。
对于给定的装备和装饰珠数据,求解如何镶嵌装饰珠,使得 6 件装备能得到的总价值达到最大。
输入描述
输入的第 1 至 6 行,包含 6 件装备的描述。其中第 i 行的第一个整数 表示第 i 件装备的装饰孔数量。后面紧接着
个整数,分别表示该装备上每个装饰孔的等级
。
第 7 行包含一个正整数 ,表示装饰珠 (技能) 种类数量。
第 8 至 行,每行描述一种装饰珠 (技能) 的情况。每行的前两个整数
和
分别表示第
种装饰珠的等级和上限。接下来
个整数,其中第
个数表示该装备中装饰珠数量为
时的价值
。
其中, 。
输出描述
输出一行包含一个整数,表示能够得到的最大价值。
输入输出样例
示例
输入
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
题目来源:装饰珠
1、题目分析
6件装备不重要,重要的是装饰孔。我们可以将上述问题表达成:装饰孔有不大于4种类型,共有total个装饰孔,现有M种装饰珠,问将这些装饰珠放入装饰孔中的最大价值是多少?
其中,装饰珠每颗装饰珠的价值不是相等的,它是随着装饰珠的个数增加而增加。但是到达阈值之后就不再增加。
2、动态转移数组
dp[ i ] [ j ] : 表示前 i 种装饰珠 放入 j 个装饰孔中 所能产生的最大价值
基本图形如上图所示,与一般的分组背包不同的是:
- 经典分组背包的空间只有一种类型,本题有四种类型,分别为装饰孔的
- 经典分组背包将物品进行分组,本题也将装饰珠进行分组
- 经典分组背包内每个物品的价值都是固定的,本题是1个、2个...物品的价值,第2个物品没有明确的价值,必须与第1个一起
- 经典分组背包每个物品的体积是固定的且可能有差别,本题采用将装饰珠分为4个等级,与装饰孔的等级对应。低级别的装饰珠可以放入装饰孔中,但是高等级的装饰珠不能放入低等级的装饰孔
- 经典分组背包问题中每一组只能放入一个物品,而本题可以放入多个物品。例如:1[value:2,weight:1] 1,2 [value:5,weight:1] 1,2,3[value:8,weight:1]。我们可以直接把它看做:1[value:2,weight:1] 2 [value:5,weight:2] 3 [value:5,weight:3].这样我们就将这个题目改造成了经典的分组背包问题。每个物品的价值和重量都不同了
- 经典背包问题每组只装入一个物品,本题在空间足够的情况下,每一组尽量装入上限个物品
可以从等级高->等级低的顺序对装饰孔开始放入装饰珠,这样子放入的原因是,在进行动态转移数组更新的时候,等级高的先进行 “该装饰孔被放入 | 该装饰孔不放入” ,这样在 “装饰孔未被放入时” ,就可以有机会将低等级的装饰珠放入。
在放入装饰孔时,我们优先将和装饰孔同等级的装饰珠放入装饰孔。或者说等级的装饰珠朱能放入
等级的装饰孔,
等级的装饰珠朱能放入
等级的装饰孔,...,
等级的装饰珠朱能放入
等级的装饰孔,我们优先放入对应等级的装饰孔,如果装饰珠还有多余,那么再向高等级装饰孔放入装饰珠。
在[2] 背包问题之分组背包中,在对每一行(即每一个分组)操作之前,它有两种方法。第一种,把前一行的数值全部复制平移到下一行来,其结果也就是 “” 。还有一种,没有平移,每次访问上一行的数据。在本题中表现为 : dp[kind][m] = Math.max(dp[kind][m], dp[kind-1][m-k]+w[j][k]); 红色字体表示方法的不同。 本题采用将上一行(上一组)数据平移到下一行(下一组)。
更新状态转移方程
//更新动态转移数组
for(int k=1;k<=pbead[j];k++) {//装饰珠的个数
for(int m=holeSum;m>=k;m--) {//剩余的空间数//不装入和装入哪个的价值更高
dp[kind][m] = Math.max(dp[kind][m], dp[kind-1][m-k]+w[j][k]);
}
}
动态效果展示:
代码:
import java.util.Scanner;
public class _装饰珠 {
public static void main(String[] args) {
System.out.println("Please input data:");
Scanner inputScanner =new Scanner(System.in);
//存储各个等级的孔的个数
int hole[] = new int[5];
//总孔数
int total=0;
for(int i=1;i<=6;i++) {
//存储第i件装备孔的数量
int num = inputScanner.nextInt();
for(int j=1;j<=num;j++) {
hole[inputScanner.nextInt()]++;
total++;
}
}
int M = inputScanner.nextInt();
//存储每个装饰珠数量为K时的价值
int w[][] = new int[M+5][8];
//存储装饰珠的上限
int pbead[] = new int[M+5];
//存储装饰珠的等级
int lBbead[] = new int[M+5];
for(int i=1;i<=M;i++) {
lBbead[i] = inputScanner.nextInt();
pbead[i] = inputScanner.nextInt();
for(int j=1;j<=pbead[i];j++) {
w[i][j] = inputScanner.nextInt();
}
}
//动态转移数组,dp[i][j]表示前i类的装饰珠放入j个孔所能产生的最大价值
int dp[][] = new int[M+1][total+1];
int holeSum=0, kind=0;
for(int i=4;i>=1;i--) {//孔的类型从大到小
holeSum += hole[i];
if(holeSum == 0) continue;
for(int j=1;j<=M;j++) {//珠子的种类
if(lBbead[j] == i) {//优先填入与等级一致的珠子
kind++;
//纵向平移,初始化
for(int k=1;k<=holeSum;k++)
dp[kind][k] = dp[kind-1][k];
//更新动态转移数组
for(int k=1;k<=pbead[j];k++) {//把装饰珠个数不同价值不同直接看做价值不同的珠子
for(int m=holeSum;m>=k;m--) {
dp[kind][m] = Math.max(dp[kind][m], dp[kind-1][m-k]+w[j][k]);
}
}
}
}
}
int res=0;
for(int i1=1;i1<=total;i1++)
res = Math.max(res, dp[kind][i1]);
System.out.println(res);
}
}