题解 | 史上最全解析,0-1背包问题加了一些情况#购物单#
购物单
https://www.nowcoder.com/practice/f9c6f980eeec43ef85be20755ddbeaf4
之前没做过动态规划,这道题理解了我两天,其实就是很简单的01背包问题,就是每个物品都会有四种情况,然后就是不加入物品或者加入物品的四种情况中的一种,下面是题解和最详细的代码注释
/**
* @author TanJie
* @date 2023/10/1 17:16
*/
import java.util.Arrays;
import java.util.Scanner;
/**
* 输入的第 1 行,为两个正整数N,m,用一个空格隔开:
* (其中 N ( N<32000 )表示总钱数, m (m <60 )为可购买的物品的个数。)
* 从第 2 行到第 m+1 行,第 j 行给出了编号为 j-1 的物品的基本数据,每行有 3 个非负整数 v p q
* (其中 v 表示该物品的价格( v<10000 ), p 表示该物品的重要度( 1 ~ 5 ),
* q 表示该物品是主件还是附件。如果 q=0 ,表示该物品为主件,如果 q>0 ,表示该物品为附件, q 是所属主件的编号)
*/
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int money = scanner.nextInt(); //总金额
int goods_num = scanner.nextInt(); //商品数量
if (money == 0 || goods_num == 0) {
System.out.println(0);
}
//建立商品数组,下标就是表示商品编号,所以数组长度加1,商品编号从1开始
Good[] goods = new Good[goods_num + 1];
//i代表商品的顺序编号 设置所有商品的基础属性
for (int i = 1; i <= goods_num; i++) {
int price = scanner.nextInt();
int importance = scanner.nextInt();
int annexId = scanner.nextInt(); //0:主件,>0: 附件
int satisfaction = price * importance; //满意度
goods[i] = new Good(price, importance, annexId, satisfaction);
}
for (int i = 1; i <= goods_num; i++) {
//如果是附件
if (goods[i].annexId > 0) {
if (goods[goods[i].annexId].a1id == 0) {
goods[goods[i].annexId].a1id = i;
} else {
goods[goods[i].annexId].a2id = i;
}
}
}
//计算所有主件的关联属性,把四种情况合并为一种
for (int i = 1; i <= goods_num; i++) {
//如果是主件,计算这个主件所对应四种情况的所有价格表,满意度
if (goods[i].annexId == 0) {
if (goods[i].a1id != 0) {
goods[i].price1 = goods[i].price + goods[goods[i].a1id].price;
goods[i].satisfaction1 = goods[i].satisfaction + goods[goods[i].a1id].satisfaction;
}
if (goods[i].a2id != 0) {
goods[i].price2 = goods[i].price + goods[goods[i].a2id].price;
goods[i].satisfaction2 = goods[i].satisfaction + goods[goods[i].a2id].satisfaction;
}
if (goods[i].a1id != 0 && goods[i].a2id != 0) {
//这里不能简单的用price1+price2-price的方式来解决,因为prices1和price2都可能为0,satisfaction同理
// goods[i].priceAll = goods[i].price1 + goods[i].price2 - goods[i].price;
// goods[i].satisfactionALL = goods[i].satisfaction1 + goods[i].satisfaction2 - goods[i].satisfaction;
goods[i].priceAll = goods[i].price + goods[goods[i].a1id].price + goods[goods[i].a2id].price;
goods[i].satisfactionALL = goods[i].satisfaction + goods[goods[i].a1id].satisfaction + goods[goods[i].a2id].satisfaction;
}
}
}
//定义状态转移数组,该问题转化为01背包问题
//商品数量从1遍历到goods_num, 金额大小从0遍历到money
int[][] dp = new int[goods_num + 1][money + 1]; //dp[0][] = 0 第一行初始化为0,从第2行开始遍历
//从i个物品里面挑选,选出满意度最大的组合
for (int i = 1; i <= goods_num; i++) {
//当金额为j时,满意度最大的组合
for (int j = 0; j <= money; j++) {
//不管是不是附件,每次都要更新为前一个状态,不然dp[i][j]就是0,
// 然后就是放入这四种状态中的一种和不不放入比较,也就是上一次的状态
dp[i][j] = dp[i - 1][j];
//如果是附件,则不参与挑选
if (goods[i].annexId != 0) {
continue;
}
//对于第i个物品,当金额为j时最大满意度, 对于每个i,又分为这四种情况,在这四种情况中选
//因为这四种情况是顺序执行,所以要在当前状态中比较最大值
//dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - p] + s);
if (j >= goods[i].price) {
dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - goods[i].price] + goods[i].satisfaction);
}
if (goods[i].a1id != 0 && j >= goods[i].price1) {
dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - goods[i].price1] + goods[i].satisfaction1);
}
if (goods[i].a2id != 0 && j >= goods[i].price2) {
dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - goods[i].price2] + goods[i].satisfaction2);
}
if (goods[i].a1id != 0 && goods[i].a2id != 0 && j >= goods[i].priceAll) {
dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - goods[i].priceAll] + goods[i].satisfactionALL);
}
}
}
//最大满意度为数组中最后一个元素
System.out.println(dp[goods_num][money]);
// System.out.println(Arrays.deepToString(dp));
}
}
/**
* 商品类
*/
class Good {
int price; //价格
int price1; //主件+附件1价格
int price2; //主件+附件2价格
int priceAll; //主键+附件1和2的价格
int importance; //重要度
int annexId; //商品编号 0为主商品,其他为编号为该id的主商品附件
int satisfaction; //满意度
int satisfaction1; //主件加附件1的满意度
int satisfaction2; //主件加附件2的满意度
int satisfactionALL; //主件加附件1和2的满意度
int a1id; //附件1 商品编号
int a2id; //附件2 商品编号
public Good(int price, int importance, int annexId, int satisfaction) {
this.price = price;
this.importance = importance;
this.annexId = annexId;
this.satisfaction = satisfaction;
}
}
#动态规划背包01问题#
查看20道真题和解析