描述
题目来自这里:www.nowcoder.com/practice/f9…
王强决定把年终奖用于购物,他把想买的物品分为两类:主件与附件,附件是从属于某个主件的,下表就是一些主件与附件的例子:
| 主件 | 附件 |
|---|---|
| 电脑 | 打印机,扫描仪 |
| 书柜 | 图书 |
| 书桌 | 台灯,文具 |
| 工作椅 | 无 |
如果要买归类为附件的物品,必须先买该附件所属的主件,且每件物品只能购买一次。
每个主件可以有 0 个、 1 个或 2 个附件。附件不再有从属于自己的附件。
王强查到了每件物品的价格(都是 10 元的整数倍),而他只有 N 元的预算。除此之外,他给每件物品规定了一个重要度,用整数 1 ~ 5 表示。他希望在花费不超过 N 元的前提下,使自己的满意度达到最大。
满意度是指所购买的每件物品的价格与重要度的乘积的总和,假设设第ii件物品的价格为v[i]v[i],重要度为w[i]w[i],共选中了kk件物品,编号依次为j_1,j_2,...,j_kj1,j2,...,jk,则满意度为:v[j_1]*w[j_1]+v[j_2]*w[j_2]+ … +v[j_k]*w[j_k]v[j1]∗w[j1]+v[j2]∗w[j2]+…+v[jk]∗w[jk]。(其中 * 为乘号)
请你帮助王强计算可获得的最大的满意度。
解题
我是用的常用解题思路来解的:
for 状态1 in 状态1的所有取值:
for 状态2 in 状态2的所有取值:
for ... dp[状态1][状态2][...] = 择优(选择1,选择2...)
不过在这之前先找到了数组的最大公因数,让各数据除以公因数,最后计算的结果乘以公因数就可以了,下面是具体代码:
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int totalMoney = sc.nextInt();
int totalNum = sc.nextInt();
int[] moneys = new int[totalNum];
int[] importants = new int[totalNum];
int[] parents = new int[totalNum];
for(int i=0;i<totalNum;i++){
moneys[i] = sc.nextInt();
importants[i] = sc.nextInt();
parents[i] = sc.nextInt();
}
//求出最大公因数
int gys = maxGys(moneys);
totalMoney = totalMoney / gys;
for(int i=0;i<moneys.length;i++){
moneys[i] = moneys[i] / gys;
}
Node[][] dp = new Node[totalNum+1][totalMoney+1];
for(int i = 0;i<=totalNum;i++){
for(int m = 0;m <= totalMoney;m++){
if(i == 0 || m == 0){
dp[i][m] = new Node();
continue;
}
dp[i][m] = new Node();
if(m - moneys[i-1] < 0){
dp[i][m] = dp[i-1][m];
}else{
if(parents[i-1] == 0){//父节点
int lastV = dp[i-1][m].value;
int andV = dp[i-1][m-moneys[i-1]].value + (moneys[i-1] * importants[i-1]);
if(lastV > andV){
dp[i][m] = dp[i-1][m];
}else{
dp[i][m].value = andV;
//把父节点记录进去
dp[i][m].parents.add(i);
dp[i][m].parents.addAll(dp[i-1][m-moneys[i-1]].parents);
}
}else{//子节点
if(dp[i-1][m-moneys[i-1]].parents.contains(parents[i-1])){//里面有该子节点的父节点
int lastV = dp[i-1][m].value;
int andV = dp[i-1][m-moneys[i-1]].value + (moneys[i-1] * importants[i-1]);
if(lastV > andV){
dp[i][m] = dp[i-1][m];
}else{
dp[i][m].value = andV;
//把父节点记录进去
dp[i][m].parents.addAll(dp[i-1][m-moneys[i-1]].parents);
}
}else{//无,则继续判断
if(m - moneys[i-1] - moneys[parents[i-1]-1] < 0){//不够添加父节点
dp[i][m] = dp[i-1][m];
}else{//够添加
int lastV = dp[i-1][m].value;
if(dp[i-1][m-moneys[i-1]-moneys[parents[i-1]-1]].parents.contains(parents[i-1])){//已经含有该父节点了
int andV = dp[i-1][m-moneys[i-1]-moneys[parents[i-1]-1]].value + (moneys[i-1] * importants[i-1]);
if(lastV > andV){
dp[i][m] = dp[i-1][m];
}else{
dp[i][m].value = andV;
//把父节点记录进去
dp[i][m].parents.addAll(dp[i-1][m-moneys[i-1]-moneys[parents[i-1]-1]].parents);
}
}else{
int andV = dp[i-1][m-moneys[i-1]-moneys[parents[i-1]-1]].value + (moneys[i-1] * importants[i-1]) + moneys[parents[i-1]-1] * importants[parents[i-1]-1];
if(lastV > andV){
dp[i][m] = dp[i-1][m];
}else{
dp[i][m].value = andV;
//把父节点记录进去
dp[i][m].parents.add(parents[i-1]);
dp[i][m].parents.addAll(dp[i-1][m-moneys[i-1]-moneys[parents[i-1]-1]].parents);
}
}
}
}
}
}
}
}
System.out.println(dp[totalNum][totalMoney].value * gys);
}
/**
* 满意度 类
*/
static class Node{
//满意度
public int value;
//含有的父节点
public List<Integer> parents = new ArrayList<>();
}
/**
* 最大公因数
*/
private static int maxGys(int[] moneys){
if(moneys.length == 1){
return moneys[0];
}
int temp = moneys[0];
for(int i = 1;i<moneys.length;i++){
temp = gcd(temp,moneys[i]);
}
return temp;
}
//迭代计算两个最大公因数的方法
private static int gcd(int m,int n){
if(n == 0){
return m;
}
int r = m%n;
return gcd(n,r);
}
}