题解 | 史上最全解析,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问题#
全部评论

相关推荐

点赞 评论 收藏
分享
评论
2
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务