当前位置: 首页 > news >正文

【动态规划】详解混合背包问题

目录

  • 1. 前置文章
  • 2. 题目
  • 3. 小结

1. 前置文章

本文前置文章:

  • 【动态规划】详解 0-1背包问题
  • 【动态规划】详解完全背包问题
  • 【动态规划】详解分组背包问题
  • 【动态规划】详解多重背包问题

下面是三种背包模式的区别:

  • 0 - 1 背包 是说:有 n 个物品和一个重量为 t 的背包,这 n 个物品中第 i 个物品的重量为 w[i],价值为 v[i],那么这个背包能装下的物品最大价值是多少,注意一个物品只能选一次。
  • 完全背包 是说:有 n 个物品和一个重量为 t 的背包,这 n 个物品中第 i 个物品的重量为 w[i],价值为 v[i],那么这个背包能装下的物品最大价值是多少,注意一个物品可以选无数次。
  • 分组背包 是说:有 n 组物品和一个重量为 t 的背包,每个物品都有自己的体积,价值和组号,代表这个物品属于哪一组,要求在不超过背包容量的前提下,从每组物品中最多选择一个物品,使得背包中物品的总价值最大。
  • 多重背包 是说:有 n 个物品和一个重量为 t 的背包,这 n 个物品中第 i 个物品的重量为 w[i],价值为 v[i],数量为 c[i],那么这个背包能装下的物品最大价值是多少,注意一个物品可以选择的次数限制为 c[i]。

2. 题目

因为混合背包其实就是一道题中包括了 0-1 背包,完全背包,多重背包,这里直接看一道题:

  • coins

这道题目意思是:题目输入 n, m,并且给出了这 n 个硬币的价值和个数,问通过这些硬币能够构成 [1,m] 这个价值范围里面的哪些价值。

举个例子:假设 m = 5,现在给了两种硬币,硬币1 价值 1 个数2,硬币2 价值 4 个数 1,问 [1,5] 这个范围里面有多少个价值能够被这些硬币凑出来。

  • 首先 1 肯定是可以的,因为价值为1 的硬币有 2 个
  • 同理 2 也是可以的
  • 3 不可以,因为硬币1 和 硬币2 凑不出来价值 3
  • 4 可以,只需要一个硬币2 就行了
  • 5 也可以,只需要一个硬币2 和一个硬币 1

所以最终答案就是 5。

这道题就是混合背包的题目了,同样外层遍历物品,内层遍历背包。

  • 物品个数为 1,就是 0-1 背包了。
  • 物品个数 * 价值 > m,就说明此时这些物品总价值已经超过了上限 m,是完全背包模板。
  • 物品个数 * 价值 <= m,就按多重背包来算。
import java.util.Arrays;
import java.util.Scanner;

public class Main {

    public static int MAXN = 101;

    public static int MAXM = 100001;

    public static int[] v = new int[MAXN];
    public static int[] c = new int[MAXN];

    public static boolean[] dp = new boolean[MAXM];

    public static int n, m = 0;

    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        while (scan.hasNext()) {
            n = scan.nextInt();
            m = scan.nextInt();
            if (n != 0 || m != 0) {
                for (int i = 1; i <= n; i++) {
                    v[i] = scan.nextInt();
                }
                for (int i = 1; i <= n; i++) {
                    c[i] = scan.nextInt();
                }
            }
            System.out.println(compute());
        }
    }

    /**
     * 有 n 个硬币, 给了出 n 个硬币的价值和个数, 价值相当于重量了
     * 再给一个数字 m,求 [1, m] 这个区间内有多少是能够被这些硬币拼凑出来
     *
     * @return
     */
    public static int compute() {
        // 每次求之前都初始化一下
        Arrays.fill(dp, false);
        dp[0] = true;

        for (int i = 1; i <= n; i++) {
            if (c[i] == 1) {
                // 物品只有一个,01 背包
                for (int j = m; j >= v[i]; j--) {
                    if (dp[j - v[i]]) {
                        dp[j] = true;
                    }
                }
            } else if (v[i] * c[i] > m) {
                // 完全背包
                for (int j = v[i]; j <= m; j++) {
                    if (dp[j - v[i]]) {
                        dp[j] = true;
                    }
                }
            } else {
                // 多重背包: v[i] * c[i] < m
                // 因为这里我们需要标记 dp 里面 1~m 哪个是 true,所以单调队列就用一个变量表示 true 的个数就行了
                for (int mod = 0; mod <= Math.min(m, v[i] - 1); mod++) {
                    int trueCount = 0;
                    int count = 1;
                    for (int j = m - mod; j >= 0 && count <= c[i]; j -= v[i], count++) {
                        trueCount += dp[j] ? 1 : 0;
                    }

                    for (int j = m - mod, last = j - c[i] * v[i]; j >= 0; j -= v[i], last -= v[i]) {
                        if (last >= 0) {
                            trueCount += dp[last] ? 1 : 0;
                        }
                        if (dp[j]) {
                            // 如果已经是 true 了,那么相当于直接删除单调队列里面的过期元素
                            trueCount--;
                        } else {
                            if(trueCount > 0){
                                dp[j] = true;
                            }
                        }
                    }
                }
            }
        }

        int ans = 0;
        for (int i = 1; i < dp.length; i++) {
            if (dp[i]) {
                ans++;
            }
        }
        return ans;
    }

}

上面计算多重背包的时候用了 trueCount 代替单调队列,因为我们不需要记录最大值,只需要记录当前 dp[j] 能不能变成 true,只要依赖的几个有一个为 true 那么 dp[j] 就能变成 true。

当然如果 dp[j] 已经是 true 了,那么 j 继续往前遍历,这种情况下 j 下标的值就过期了,所以需要 trueCount--


3. 小结

这道题就是混合背包,里面涉及到了 0-1 背包,完全背包,多重背包的流程,还是一样,参考视频:算法讲解075【必备】背包dp-多重背包、混合背包。





如有错误,欢迎指出!!!

相关文章:

  • YOLO11改进-模块-引入空间带状注意力机制(Spatial Strip Attention,SSA)增强模型对空间信息处理能力的重要模块
  • Windows搭建免翻墙的BatteryHistorian
  • Animation - AI Controller控制SKM_Manny的一些问题
  • 笔记:代码随想录算法训练营day58:101.孤岛的总面积、102.沉没孤岛、103.水流问题、104.建造最大岛屿
  • 成都文创浪潮中,国际数字影像产业园的标杆塑造之路
  • Netlify 的深度解析及使用指南
  • 深入探讨 `ip2region` 中三种初始化方法:newWithBuffer、newWithVectorIndex 和 newWithFileOnly
  • 针对耳鸣患者推荐的一些菜谱和食材
  • 产品更新 | 数字助决胜:华望M-Arch平台实现从体系模型到仿真推演
  • ManiWAV:通过野外的音频-视频数据学习机器人操作
  • C#自定义曲线便器功能实现(简化版)
  • PTA团体程序设计天梯赛-练习集46-50题
  • qt程序打包成一个文件
  • Chapter 8 Charge Pump
  • Jmeter分布式集群压测
  • 什么是反射以及反射机制优缺点
  • UR5e机器人位姿
  • 手机录视频风噪太大?华为Pura X“AI降风噪“太硬核了
  • ISSN号是什么?连续出版物标识的应用与生成
  • 算法 | 优化算法比较
  • 九家企业与上海静安集中签约,投资额超10亿元
  • 招行:拟出资150亿元全资发起设立金融资产投资公司
  • 湖南省邵阳市副市长仇珂静主动向组织交代问题,接受审查调查
  • 明天起,沪苏湖高铁、杭温高铁推出13款新型票制产品
  • 外交部回应中美经贸高层会谈:这次会谈是应美方请求举行的
  • 专访|李沁云:精神分析不会告诉你“应该怎么做”,但是……