题目描述
王强今天很开心,公司发给N元的年终奖。王强决定把年终奖用于购物,他把想买的物品分为两类:主件与附件,附件是从属于某个主件的,下表就是一些主件与附件的例子:
主件 附件
电脑 打印机,扫描仪
书柜 图书
书桌 台灯,文具
工作椅 无
如果要买归类为附件的物品,必须先买该附件所属的主件。每个主件可以有 0 个、 1 个或 2 个附件。附件不再有从属于自己的附件。王强想买的东西很多,为了不超出预算,他把每件物品规定了一个重要度,分为 5 等:用整数 1 ~ 5 表示,第 5 等最重要。他还从因特网上查到了每件物品的价格(都是 10 元的整数倍)。他希望在不超过 N 元(可以等于 N 元)的前提下,使每件物品的价格与重要度的乘积的总和最大。
设第 j 件物品的价格为 v[j] ,重要度为 w[j] ,共选中了 k 件物品,编号依次为 j 1 , j 2 ,……, j k ,则所求的总和为:
v[j 1 ]*w[j 1 ]+v[j 2 ]*w[j 2 ]+ … +v[j k ]*w[j k ] 。(其中 * 为乘号)
请你帮助王强设计一个满足要求的购物单。
输入描述:
输入的第 1 行,为两个正整数,用一个空格隔开:N m
(其中 N ( <32000 )表示总钱数, m ( <60 )为希望购买物品的个数。)
从第 2 行到第 m+1 行,第 j 行给出了编号为 j-1 的物品的基本数据,每行有 3 个非负整数 v p q
(其中 v 表示该物品的价格( v<10000 ), p 表示该物品的重要度( 1 ~ 5 ), q 表示该物品是主件还是附件。如果 q=0 ,表示该物品为主件,如果 q>0 ,表示该物品为附件, q 是所属主件的编号)
输出描述:
输出文件只有一个正整数,为不超过总钱数的物品的价格与重要度乘积的总和的最大值( <200000 )。
示例1
输入
1000 5
800 2 0
400 5 1
300 5 1
400 3 0
500 2 0
输出
2200
代码
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
pulic class Main{
public static class Good {
public int v;//价格
public int p;//重要度
public int q;//是否是附件
public int a1 = 0;//附件1 id
public int a2 = 0;//附件2 id
public Good() {
}
public Good(int v, int p, int q) {
this.v = v;
this.p = p;
this.q = q;
}
public void setA1(int a1) {
this.a1 = a1;
}
public void setA2(int a2) {
this.a2 = a2;
}
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String mn = scanner.nextLine();
String[] arr1 = mn.split(" ");
//钱,除去10方便计算,下面的每件物品的价格也都会除去10,最终输出结果会乘10
int n = Integer.valueOf(arr1[0]) / 10;
int m = Integer.valueOf(arr1[1]);//数量
Good[] gs = new Good[m + 1];
for (int i = 1; i <= m; i++) {
String vpq = scanner.nextLine();
String[] vpqArr = vpq.split(" ");
int v = Integer.valueOf(vpqArr[0]) / 10;//价格
int p = Integer.valueOf(vpqArr[1]);//重要度
int q = Integer.valueOf(vpqArr[2]);//是否是主件
//创建货物
gs[i] = new Good(v, p, q);
if (q > 0) {
//如果是附件,则将其在数组中的位置保存到主件中
if (gs[q].a1 == 0) {
//附件1
gs[q].setA1(i);
} else {
//附件2
gs[q].setA2(i);
}
}
}
//开始动态规划
int[][] dum = new int[m + 1][n + 1];
//按物品循环
for (int i = 1; i <= m; i++) {
//计算每件商品的 v*p,如果有附件则需要加上附键的 v*p
//三种情况,计算价格乘重要度的分数
int v0 = 0, v1 = 0, v2 = 0, v3 = 0, temp0 = 0, temp1 = 0, temp2 = 0, temp3 = 0;
// 主体
v0 = gs[i].v;
temp0 = v0 * gs[i].p;
// 主+次1
if (gs[i].a1 > 0) {
v1 = v0 + gs[gs[i].a1].v;
temp1 = temp0 + gs[gs[i].a1].v * gs[gs[i].a1].p;
}
// 主+次2
if (gs[i].a2 > 0) {
v2 = v0 + gs[gs[i].a2].v;
temp2 = temp0 + gs[gs[i].a2].v * gs[gs[i].a2].p;
}
// 主+次12
if (gs[i].a1 > 0 && gs[i].a2 > 0) {
v3 = v1 + v2 - v0;
temp3 = temp1 + temp2 - temp0;
}
//按目前所有金额循环
for (int j = 1; j <= n; j++) {
//附件循环时可忽略,因为只是复制主件的数据
if (gs[i].q > 0) {
dum[i][j] = dum[i - 1][j];
} else {
//下方按上面四种情况循环
dum[i][j] = dum[i - 1][j];
//当物品金额足够买当前组合时,当前数量物品的计分区间就会重合,然后叠加分数
//当前主件和其他主件组合之间计分
if (j >= v0 && v0 != 0) {
dum[i][j] = Math.max(dum[i][j], dum[i - 1][j - v0] + temp0);
}
//当前主件和附件1与其他主件组合的计分
if (j >= v1 && v1 != 0) {
dum[i][j] = Math.max(dum[i][j], dum[i - 1][j - v1] + temp1);
}
//当前主件和附件2与其他主件组合的计分
if (j >= v2 && v2 != 0) {
dum[i][j] = Math.max(dum[i][j], dum[i - 1][j - v2] + temp2);
}
//当前主件和附件1加附件2与其他主件组合的计分
if (j >= v3 && v3 != 0) {
dum[i][j] = Math.max(dum[i][j], dum[i - 1][j - v3] + temp3);
}
}
}
}
//最终最佳结果会累加在二次数列的右下角,也就是dum[m][n],乘上之前除去的10,输出结果
System.out.println(dum[m][n] * 10);
}
}