题目描述
在怪物猎人这一款游戏中,玩家可以通过给装备镶嵌不同的装饰珠来获取 相应的技能,以提升自己的战斗能力。
已知猎人身上一共有 6 件装备,每件装备可能有若干个装饰孔,每个装饰孔有各自的等级,可以镶嵌一颗小于等于自身等级的装饰珠 (也可以选择不镶嵌)。
装饰珠有 M 种,编号 1 至 M,分别对应 M 种技能,第 i种装饰珠的等级为 Li,只能镶嵌在等级大于等于 Li 的装饰孔中。
对第 i 种技能来说,当装备相应技能的装饰珠数量达到 Ki 个时,会产生 Wi(Ki) 的价值。镶嵌同类技能的数量越多,产生的价值越大,即 Wi(Ki−1)<Wi(Ki)。但每个技能都有上限 Pi(1≤Pi≤7),当装备的珠子数量超过 P_iPi 时,只会产生 Wi(Pi) 的价值。
对于给定的装备和装饰珠数据,求解如何镶嵌装饰珠,使得 6 件装备能得到的总价值达到最大。
输入描述
输入的第 1 至 6 行,包含 6 件装备的描述。其中第 ii 行的第一个整数 N_iNi 表示第 ii 件装备的装饰孔数量。后面紧接着 Ni 个整数,分别表示该装备上每个装饰孔的等级 (1≤L≤4)。
第 7 行包含一个正整数 M,表示装饰珠 (技能) 种类数量。
第 8 至 M + 7 行,每行描述一种装饰珠 (技能) 的情况。每行的前两个整数Lj (1≤Lj≤4) 和Pj (1≤Pi≤7) 分别表示第 j 种装饰珠的等级和上限。接下来 Pj 个整数,其中第 kk 个数表示该装备中装饰珠数量为 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 装饰珠 W1(4)+W2(4)=5+15=20。
题目分析
首先数据量很大,不用穷举和模拟算法。这个装与不装的问题,类似背包问题,我们可以选择动态规划来解决这个题。
每次添加一种装饰珠来计算最大价值。
dp[i][j] i代表每一种装饰珠 j代表容量
我们可以得到dp[i][j] = max(dp[i-1][j],dp[i-1][j-k]+W(ik))(k代表第i种装饰珠的选择个数)
算法:
1.从大等级到小等级遍历个等级孔(因为低等级的孔不能装高等级的球)
2.遍历所有的装饰珠
3.如果等级相同则进入动态算法。
动态每一个孔
动态容量
4.选取最后的最大值作为答案
输入数据比较大,我们可以选择快速输入来加速算法运行速度。
import java.io.*;
import java.util.Arrays;
public class Main {
static StreamTokenizer re = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
//static Scanner sc = new Scanner(System.in);
static int nextInt() throws IOException {
re.nextToken();
return (int) re.nval;
}
public static void main(String[] args) throws IOException {
int[] canPut = new int[5];//各等级孔的数量
int size = 0;// 想要的孔
for (int i = 0; i < 6; i++) {//统计孔数量
int n = nextInt();
for (int j = 0; j < n; j++) {
canPut[nextInt()]++;
size++;
}
}
int type = nextInt();// 球种类的数目
int[][] data = new int[type + 1][size + 1];//动态规划数组
int[][] worth = new int[type + 1][9];//0:等级 1:数量 记录球
for (int i = 1; i <= type; i++) {
worth[i][0] = nextInt();
worth[i][1] = nextInt();
for (int j = 2; j < worth[i][1]+2; j++) {
worth[i][j] = nextInt();
}
}
int curSize = 0; // 记录大于等于等级的珠子数目
int index = 1; // 记录已经算过的珠子
for (int i = 4; i >= 1; i--) {
if (canPut[i] == 0) continue;//如果没有球就跳过
curSize += canPut[i];
for (int x = 1; x < worth.length; x++) { //遍历worth(所有种类的球)表,拿出等级匹配的
if (worth[x][0] == i) { //当前物品可放
for (int y = 1; y <= curSize; y++) {//y:容量 遍历所有的能装的孔
int max = data[index - 1][y];
for (int count = 1; count <= Math.min(y, worth[x][1]); count++) {
max = Math.max(max, data[index - 1][y - count] + worth[x][count + 1]);//当前物品放若干个都进行判断
}
data[index][y] = max;
}
index++;
}
}
}
Arrays.sort(data[index-1]);
System.out.println((data[index - 1][size]));
}
}