欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

❤重点❤】【牛客网 - 华为整机测试】HJ16 购物清单 - 动态规划

最编程 2024-04-19 18:50:49
...

HJ16 购物单

题目描述

在这里插入图片描述
在这里插入图片描述

解题思路

这题其实是0-1背包问题,但是附加了条件

具体思路就是构造物品类,然后对主件判断是否有附件,若有附件则依次添加,根据主件、附件1、附件2的组合有四种情况

  • 只有主件
  • 主件+附件1
  • 主件+附件2
  • 主件+附件1+附件2

根据以上情况转化问题为经典的 01背包问题 ,接着就是套公式动态规划即可

经典背包问题回顾

问题描述:有一个背包可以装物品的总重量为W,现有N个物品,每个物品中w[i],价值v[i],用背包装物品,能装的最大价值是多少?

定义状态转移数组dp[i][j],表示前i个物品,背包重量为j的情况下能装的最大价值。

例如,dp[3][4]=6,表示用前3个物品装入重量为4的背包所能获得的最大价值为6,此时并不是3个物品全部装入,而是3个物品满足装入背包的条件下的最大价值。

状态转移方程:

dp[i][j] = max(dp[i-1][j], dp[i-1][j-w[i]]+v[i])

dp[i-1][j]表示当前物品不放入背包,dp[i-1][j-w[i]]+v[i]表示当前物品放入背包,即当前第i个物品要么放入背包,要么不放入背包。

dp = [[0]*(n+1) for _ in range(m+1)]
for i in range(1,m+1):
    for j in range(1,n+1):
        if j-w[i]>=0:
            dp[i][j] = max(dp[i-1][j], dp[i-1][j-w[i]]+v[i])
        else:
            dp[i][j] = dp[i-1][j]
return dp[m][n]

本题解题思路

购物车本质上还是0-1背包问题,只不过多了主件和附件。

  1. 假设先不看附件,那么就和0-1背包一样了。附件不能单独出现,要依赖于主件。

     对应于背包问题,主件的个数就是物品的个数,考虑每个主件时要考虑可能出现的情况。
     
     输入例子:
     1000 5
     800 2 0
     400 5 1
     300 5 1
     400 3 0
     500 2 0
     
     在当前的例子当中物品的个数就是3。
    
  2. 考虑每个物品时要考虑每种可能出现的情况,1、主件,2、主件+附件1,3、主件+附件2,4、主件+附件1+附件2,不一定每种情况都出现,只有当存在附件时才会出现对应的情况。

     w[i][k]表示第i个物品的第k种情况,k的取值范围0~3,分别对应以上4中情况,v[i][k]表示第i个物品对应第k种情况的价值,现在就把购物车问题转化为了0-1背包问题。
     
     状态转移方程可以定义为
     
     dp[i][j] = max(dp[i-1][j],dp[i-1][j-w[i][k]]+v[i][k])
     
     dp[i-1][j]表示当前物品不放入背包,w[i][k]表示第i个主件对应第k中情况,即当前第i个物品的4中情况中价值最大的要么放入背包,要么不放入背包。
     
     需要注意:dp[i][j] = max(物品不放入背包,主件,主件+附件1,主件+附件2,主件+附件1+附件2)
    
dp = [[0]*(n+1) for _ in range(m+1)]
for i in range(1,m+1):
    for j in range(1,n+1):
        max_i = dp[i-1][j]
        for k in range(len(w[i])):
            if j-w[i][k]>=0:
                max_i = max(max_i, dp[i-1][j-w[i][k]]+v[i][k])
        dp[i][j] = max_i
print(dp[m][n])

代码

/**
 * Copyright (C), 2019-2021
 * author  candy_chen
 * date   2021/4/7 17:15
 *
 * @Classname HJ16
 * Description: 购物单
 */

import java.util.Scanner;

/**
 * 加了附加条件的背包问题
 */
public class Main {
    static class Good{
        int v;
        int vp;
        public Good(int v,int vp){
            this.v = v;
            this.vp = vp;
        }
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        //总钱数
        int N = sc.nextInt();
        //购买物品个数
        int m = sc.nextInt();
        int[] f = new int[N + 1];
        //分组,goods[i][0]为主件,goods[i][1]为附件1,goods[i][2]为附件2
        Good[][] goods1 = new Good[60][4];
        for (int i = 1; i <= m; i++) {
            int v = sc.nextInt();  //该物品的价格
            int p = sc.nextInt();  //该物品的重要度
            int q = sc.nextInt();  //该物品是主件还是附件 q=0 主件; q > 0 附件,是主件所属的编号

            Good t = new Good(v,v*p);
            if (q == 0){
                goods1[i][0] = t;
            }else {
                if (goods1[q][1] == null){
                    goods1[q][1] = t;
                }else {
                    goods1[q][2] = t;
                }
            }
        }


        for (int i = 1; i <= m; i++) {  //  m ( <60 )为希望购买物品的个数
            for (int j = N; j >= 0 && goods1[i][0] != null; j--) {
                //以下代码从分组中选择价值最大的。共五种情况:不选主件,选主件,选附件1和主件,选附件2和主件,选附件1和附件2和主件
                // 主件
                Good master = goods1[i][0];
                //情形1:不买主件
                int max = f[j];

                // 情形2: 购买主件
                if (j >= master.v && max < f[j - master.v] + master.vp) {
                    //f[j - master.v] : 当用钱买下主件后剩余的钱,能买到的最大权重
                    max = f[j - master.v] + master.vp;
                }

                // 情形3:购买主件和附件1(附件1存在即代表主件存在)
                int vt;
                if (goods1[i][1] != null) {
                    if (j >= (vt = master.v + goods1[i][1].v)
                            && max <  f[j - vt] + master.vp + goods1[i][1].vp) {
                        max = f[j - vt] + master.vp + goods1[i][1].vp;
                    }
                }
                // 附件2存在即代表主件及附件1存在)
                // 情形4: 购买主件及附件2
                if (goods1[i][2] != null) {
                    if (j >= (vt = master.v + goods1[i][1].v + goods1[i][2].v)
                            && max < f[j - vt] + master.vp + goods1[i][1].vp + goods1[i][2].vp) {
                        max = f[j - vt] + master.vp + goods1[i][1].vp + goods1[i][2].vp;
                    }
                }
                f[j] = max;
            }
        }

        System.out.println(f[N]);

    }
}