题目描述
在怪物猎人这一款游戏中,玩家可以通过给装备镶嵌不同的装饰珠来获取 相应的技能,以提升自己的战斗能力。
已知猎人身上一共有 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≤Pi≤7),当装备的珠子数量超过 P_i时,只会产生 W_i(P_i)的价值。
对于给定的装备和装饰珠数据,求解如何镶嵌装饰珠,使得 6 件装备能得到的总价值达到最大。
01背包式的动规划问题的一个变式。可以建立一个以装备装饰孔孔号为横轴,以装备号为纵轴的二维表格(数组)如下图:
表格中存放在该孔位的装饰珠序号。先把录入内存的装饰珠按升序排序,再遍历二维数组中的装饰孔。
对于每个装饰孔:
1.判断当前装饰珠的Level是否适合其孔位Level,如果不适合,则break(因为装饰珠等级升序排序,后续的装饰珠等级必定大于该装饰孔);
2.记录此时装备总价值
3.导入下一个装饰珠
4.记录装备价值,并与上一个进行比较,保留价值更大的
5.循环至遍历完成全部该装饰孔可用的装饰珠
Java代码如下:
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Scanner;
public class Zsz {
static int M;
static int[][] Wrecord;
public static void main(String[] args) {
int[][] equipment = new int[6][];
int[][] record = new int[6][];
Scanner scanner = new Scanner(System.in);
for (int i = 0; i < 6; i++) {
int n = scanner.nextInt();//当前装备装备孔的数量
int[] temp = new int[n];
//记录每个装备孔的等级
for (int j = 0; j < temp.length; j++) {
temp[j] = scanner.nextInt();
}
equipment[i] = temp;
record[i] = new int[n];
}
M = scanner.nextInt();//装饰珠的种类数量
ArrayList<int[]> W = new ArrayList<>();//记录装饰珠数量对应价值
Wrecord = new int[M][];
for (int i = 0; i < M; i++) {
int Level = scanner.nextInt();
int P = scanner.nextInt();//装饰珠数量上限
int[] temp = new int[P + 1];
temp[0] = Level;//数组首位是装饰珠等级
for (int j = 1; j < temp.length; j++) {
temp[j] = scanner.nextInt();
}
Wrecord[i] = temp.clone();
int[] temp1 = new int[P + 2];
for (int j = 0; j < temp.length; j++) {
temp1[j] = temp[j];
}
temp1[temp1.length - 1] = i;//temp1最后一位装的是装饰珠编号-1
W.add(temp1);
}
//对装饰珠进行排序
Comparator<int[]> comparator = new Comparator<int[]>() {
@Override
public int compare(int[] o1, int[] o2) {
return o1[0] - o2[0];//升序排序
}
};
Collections.sort(W, comparator);
//开始推导
for (int i = 0; i < 6; i++) {
for (int j = 0; j < equipment[i].length; j++) {
int Level = equipment[i][j];//当前孔位等级
//开始尝试安插装饰珠
//安插一个等级最小的
record[i][j] = W.get(0)[W.get(0).length - 1] + 1;
int value = Value(record);
for (int k = 1; k < W.size(); k++) {
int[] recordOfW = W.get(k);
if (recordOfW[0] > Level){//装饰孔的等级小于装饰珠的等级
break;//等级升序排序,后续的装饰珠等级必定大于装饰孔
}
int buffer = record[i][j];
int nextRecordIJ = recordOfW[recordOfW.length - 1] + 1;
record[i][j] = nextRecordIJ;
int nextValue = Value(record);
if (nextValue >= value){
//数值保留
value = nextValue;
}else {
//数值还原
record[i][j] = buffer;
}
}
//record[i][j]中保留的是最优解
}
}
System.out.println(Value(record));
}
public static int Value(int[][] record){
int value = 0;
int[] CountOfZhuang = new int[M + 1];
for (int i = 0; i < record.length; i++) {
for (int j = 0; j < record[i].length; j++) {
if (record[i][j] == 0){
//doNothing
}else {
CountOfZhuang[record[i][j]]++;
}
}
}
for (int i = 1; i < CountOfZhuang.length; i++) {
if (CountOfZhuang[i] == 0){
continue;
}
if (CountOfZhuang[i] > Wrecord[i - 1].length - 1){
//装饰珠的数量超出上限
value += Wrecord[i - 1][Wrecord[i - 1].length - 1];
}else {
value += Wrecord[i - 1][CountOfZhuang[i]];
}
}
return value;
}
}