【动态规划】装饰珠

目录

        1、题目分析

        2、动态转移数组

        参考文献


题目描述

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

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

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

对第 i 种技能来说,当装备相应技能的装饰珠数量达到 K_i​ 个时,会产生 W_i(K_i) 的价值。镶嵌同类技能的数量越多,产生的价值越大,即W_i(K_{i-1})<W_i(K_i) 。但每个技能都有上限 P_i(1<=P_i<=7) ,当装备的珠子数量超过 P_i ​ 时,只会产生 W_i(P_i) 的价值。

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

输入描述

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

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

第 8 至 M+7 行,每行描述一种装饰珠 (技能) 的情况。每行的前两个整数 L_j(1<=L_j<=4) 和 P_j(1<=P_j<=7) 分别表示第 j 种装饰珠的等级和上限。接下来P_j 个整数,其中第 k 个数表示该装备中装饰珠数量为 k 时的价值 W_j(k) 。

其中,1\leqslant N_i\leqslant 50,1\leqslant M\leqslant 10^4,1\leqslant W_j(k)\leqslant 10^4 。

输出描述

输出一行包含一个整数,表示能够得到的最大价值。

输入输出样例

示例

输入

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 个装饰孔中 所能产生的最大价值

        基本图形如上图所示,与一般的分组背包不同的是: 

  •  经典分组背包的空间只有一种类型,本题有四种类型,分别为装饰孔的L_1,L_2,L_3,L_4
  • 经典分组背包将物品进行分组,本题也将装饰珠进行分组
  • 经典分组背包内每个物品的价值都是固定的,本题是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].这样我们就将这个题目改造成了经典的分组背包问题。每个物品的价值和重量都不同了
  • 经典背包问题每组只装入一个物品,本题在空间足够的情况下,每一组尽量装入上限个物品

        可以从等级高->等级低的顺序对装饰孔开始放入装饰珠,这样子放入的原因是,在进行动态转移数组更新的时候,等级高的先进行 “该装饰孔被放入 | 该装饰孔不放入” ,这样在 “装饰孔未被放入时” ,就可以有机会将低等级的装饰珠放入。

        在放入装饰孔时,我们优先将和装饰孔同等级的装饰珠放入装饰孔。或者说L_4等级的装饰珠朱能放入L_4等级的装饰孔,L_3等级的装饰珠朱能放入L_3+L_4等级的装饰孔,...,L_1等级的装饰珠朱能放入L_1+L_2+L_3+L_4等级的装饰孔,我们优先放入对应等级的装饰孔,如果装饰珠还有多余,那么再向高等级装饰孔放入装饰珠。

        在[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);
	}

}

参考文献

[1] 蓝桥杯真题——装饰珠

[2] 背包问题之分组背包

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值