前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【动态规划/背包问题】分组背包问题

【动态规划/背包问题】分组背包问题

作者头像
宫水三叶的刷题日记
发布2021-07-22 09:58:31
1.9K0
发布2021-07-22 09:58:31
举报
文章被收录于专栏:宫水三叶的刷题日记

前言

今天是我们讲解「动态规划专题」中的「背包问题」的第十二篇

今天将会学习「分组背包」问题。

另外,我在文章结尾处列举了我所整理的关于背包问题的相关题目。

背包问题我会按照编排好的顺序进行讲解(每隔几天更新一篇,确保大家消化)。

你也先可以尝试做做,也欢迎你向我留言补充,你觉得与背包相关的 DP 类型题目 ~

分组背包

给定

N

个物品组,和容量为

C

的背包。

i

个物品组共有

S[i]

件物品,其中第

i

组的第

j

件物品的成本为

v[i][j]

,价值为

w[i][j]

每组有若干个物品,同一组内的物品最多只能选一个。

求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。

示例:

代码语言:javascript
复制
输入:N = 2, C = 9, S = [2, 3], v = [[1,2,-1],[1,2,3]], w = [[2,4,-1],[1,3,6]]

输出:10

提示:

0 < N, C, S[i] < 100

基本分析

从题面看,这似乎是一种全新的背包问题。

但其仍然是一种通过「枚举物品 - 枚举容量 - 枚举决策」来解决的组合优化问题。

经过之前 三种传统背包问题 的学习。

我们可以很轻松给出状态定义:定义

f[i][j]

为考虑前

i

个物品组,背包容量不超过

j

的最大价值

不失一般性的考虑

f[i][j]

如何计算。

由于每组有若干个物品,且每组「最多」选择一件物品。

即对于第

i

组而言,可决策的方案如下:

  • 不选择该组的任何物品:
f[i][j] = f[i - 1][j]
  • 选该组的第一件物品:
f[i][j] = f[i - 1][j - v[i][0]] + w[i][0]
  • 选该组的第二件物品:
f[i][j] = f[i - 1][j - v[i][1]] + w[i][1]
代码语言:javascript
复制
    ...
  • 选该组的最后一件件物品:
f[i][j] = f[i - 1][j - v[i][S[i] - 1]] + w[i][S[i] - 1]

显然最终的

f[i][j]

应该是从所有方案里取

max

f[i][j] = \max(f[i - 1][j], f[i - 1][j - v[i][k]] + w[i][k]), 0 <= k < S[i]

代码:

代码语言:javascript
复制
class Solution {
    public int maxValue(int N, int C, int[] S, int[][] v, int[][] w) {
        int[][] dp = new int[N + 1][C + 1];
        for (int i = 1; i <= N; i++) {
            int[] vi = v[i - 1];
            int[] wi = w[i - 1];
            int si = S[i - 1];
            for (int j = 1; j <= C; j++) {
                dp[i][j] = dp[i - 1][j];
                for (int k = 0; k < si; k++) {
                    if (j >= vi[k]) {
                        dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - vi[k]] + wi[k]);
                    }
                }
            }
        }
        return dp[N][C];
    }
}
  • 时间复杂度:
O(C * \sum_{i = 0}^{N - 1} S[i])
  • 空间复杂度:
O(N * C)

空间优化

滚动数组

根据状态转移方程,不难发现

f[i][j]

只依赖于

f[i - 1][x]

,且

j >= x

即明确只依赖上一物品组的决策结果。

因此,我们可以使用 最开始 就学过的「滚动数组」的方式,十分机械的将

O(N * C)

的空间优化到

O(C)

代码:

代码语言:javascript
复制
class Solution {
    public int maxValue(int N, int C, int[] S, int[][] v, int[][] w) {
        int[][] dp = new int[2][C + 1];
        for (int i = 1; i <= N; i++) {
            int[] vi = v[i - 1];
            int[] wi = w[i - 1];
            int si = S[i - 1];
            for (int j = 1; j <= C; j++) {
                int a = i & 1, b = (i - 1) & 1;
                dp[a][j] = dp[b][j];
                for (int k = 0; k < si; k++) {
                    if (j >= vi[k]) {
                        dp[a][j] = Math.max(dp[a][j], dp[b][j - vi[k]] + wi[k]);
                    }
                }
            }
        }
        return dp[N & 1][C];
    }
}
  • 时间复杂度:
O(C * \sum_{i = 0}^{N - 1} S[i])
  • 空间复杂度:
O(C)
一维空间优化

进一步,我们可以用「01 背包」类似的思路,进行一维空间优化:

  1. 取消物品维度;
  2. 将容量维度的遍历顺序修改为「从大到小」(确保所依赖的值不会被覆盖)。

代码:

代码语言:javascript
复制
class Solution {
    public int maxValue(int N, int C, int[] S, int[][] v, int[][] w) {
        int[] dp = new int[C + 1];
        for (int i = 1; i <= N; i++) {
            int[] vi = v[i - 1];
            int[] wi = w[i - 1];
            int si = S[i - 1];
            for (int j = C; j >= 0; j--) {
                for (int k = 0; k < si; k++) {
                    if (j >= vi[k]) {
                        dp[j] = Math.max(dp[j], dp[j - vi[k]] + wi[k]);
                    }
                }
            }
        }
        return dp[C];
    }
}
  • 时间复杂度:
O(C * \sum_{i = 0}^{N - 1} S[i])
  • 空间复杂度:
O(C)

总结

经过「一维空间」优化发现,分组背包问题的空间优化无法降低运算量。

因此我们只能采用「枚举物品 - 枚举容量 - 枚举决策」的朴素思路来求解。

但对于「组内」物品而言,由于最多只能选一件物品,因此对于成本相同的多件物品,我们应当只保留价值最大的物品,从而让总的物品数量变少。

但这样的预处理优化,也只是常数级别的优化。

事实上,分组背包问题的应用不仅仅只有「每组最多选择一件物品」的形式,还有诸如「每组必须选择一件物品」的形式。

明天我们会用一道 LeetCode 练习题来复习分组背包问题。

背包问题(目录)

  1. 01背包 : 背包问题 第一讲
    1. 【练习】01背包 : 背包问题 第二讲
    2. 【学习&练习】01背包 : 背包问题 第三讲
  2. 完全背包 : 背包问题 第四讲
    1. 【练习】完全背包 : 背包问题 第五讲
    2. 【练习】完全背包 : 背包问题 第六讲
    3. 【练习】完全背包 : 背包问题 第七讲
  3. 多重背包 : 背包问题 第八讲
  4. 多重背包(优化篇)
    1. 【上】多重背包(优化篇): 背包问题 第九讲
    2. 【下】多重背包(优化篇): 背包问题 第十讲
  5. 混合背包 : 背包问题 第十一讲
  6. 分组背包 : 本篇
    1. 【练习】分组背包 :
  7. 多维背包
    1. 【练习】多维背包
  8. 树形背包
    1. 【练习篇】树形背包
  9. 背包求方案数
    1. 【练习】背包求方案数
  10. 背包求具体方案
    1. 【练习】背包求具体方案
  11. 泛化背包
    1. 【练习】泛化背包
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-07-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 宫水三叶的刷题日记 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 分组背包
  • 基本分析
  • 空间优化
    • 滚动数组
      • 一维空间优化
      • 总结
      • 背包问题(目录)
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档