思路展示

物品分为主件和附件,考虑到一个主件最多可以购买两个附件,那我们可以细化分析,将是否购买该物品,细化为是否购买该物品,以及是否购买该物品的附件,即5种情况,不购买该物品,购买该物品,购买该物品及附件1,购买该物品及附件2,购买该物品及附件1及附件2,f[i][j]取这五种情况的最大值

牛客---HJ16 购物单(0/1背包问题的变形)_i++


牛客---HJ16 购物单(0/1背包问题的变形)_开发语言_02

示例代码

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int N = sc.nextInt();//总钱数
        int m = sc.nextInt();//总件数

        Goods[] goods = new Goods[m];
        for (int i = 0; i < m; i++) {
            goods[i] = new Goods();
        }
        for (int i = 0; i < m; i++) {
            int v = sc.nextInt();
            int p = sc.nextInt();
            int q = sc.nextInt();
            goods[i].v = v;
            goods[i].p = p * v;  // 直接用p*v,方便后面计算
            if (q == 0) {//0个附件
                goods[i].main = true;
            } else if (goods[q - 1].a1 == -1) {
                goods[q - 1].a1 = i;//找到其所属的主件
            } else {
                goods[q - 1].a2 = i;
            }
        }

        int[] dp = new int[N + 1];
        //物品分为主件和附件,考虑到一个主件最多可以购买两个附件,那我们可以细化分析,将是否购买该物品,细化为是否购买该物品,以及是否购买该物品的附件,即5种情况,不购买该物品,购买该物品,购买该物品及附件1,购买该物品及附件2,购买该物品及附件1及附件2,f[i][j]取这五种情况的最大值
        for (int i = 1; i <= m; i++) {
            for (int j = N; j >= 0; j--) {
                //只关心主件,根据主件找附件
                if (!goods[i - 1].main) {
                    continue;
                }
                if (j >= goods[i - 1].v) {
                    dp[j] = Math.max(dp[j],
                            dp[j - goods[i - 1].v] + goods[i - 1].p);
                }
                if (goods[i - 1].a1 != -1 &&
                        j >= goods[i - 1].v + goods[goods[i - 1].a1].v) {
                    dp[j] = Math.max(dp[j], 
                            dp[j -goods[i - 1].v - goods[goods[i - 1].a1].v] +
                            goods[i - 1].p + goods[goods[i - 1].a1].p);
                }
                if (goods[i - 1].a2 != -1 && j >= goods[i - 1].v +
                        goods[goods[i - 1].a2].v) {
                    dp[j] = Math.max(dp[j],
                            dp[j - goods[i - 1].v - goods[goods[i - 1].a2].v] +
                            goods[i - 1].p + goods[goods[i - 1].a2].p);
                }
                if (goods[i - 1].a1 != -1 && goods[i - 1].a2 != -1 &&
                        j >= goods[i - 1].v +
                        goods[goods[i - 1].a1].v +
                        goods[goods[i - 1].a2].v) {
                    dp[j] = Math.max(dp[j], dp[j - goods[i - 1].v -
                            goods[goods[i - 1].a1].v - goods[goods[i - 1].a2].v] +
                            goods[i - 1].p + goods[goods[i - 1].a1].p +
                                     goods[goods[i - 1].a2].p);
                }
            }
        }
        System.out.println(dp[N]);
    }
}

class Goods {
    int v;
    int p;
    boolean main = false;

    int a1 = -1;  //定义附件1的编号
    int a2 = -1;  //定义附件2的编号
}

效果展示

牛客---HJ16 购物单(0/1背包问题的变形)_开发语言_03