理解背包问题:分类与解题模板

动态规划——背包问题


理解背包问题:分类与解题模板

在算法问题中,背包问题是一类经典的动态规划问题,它们的核心思想是选择一组物品,满足某个条件或目标。背包问题不仅限于物理意义上的“背包”和“物品”,其概念可以扩展到许多实际场景,如资金分配、时间管理、资源优化等。

什么是背包问题?

背包问题可以定义为:给定一个背包容量(target)和一组物品(nums),能否按某种方式选取nums中的元素,使其总和或总重量等于target

注意:
  1. 背包容量物品可以是数值或其他类型(如字符串)。
  2. **目标值(target)**可以是显式给出,也可能需要我们从题目中推导(如sum/2等)。
  3. 选择方式包括:每个物品最多选一次、每个物品可以多次选择、物品顺序有无影响等。
背包问题的分类

背包问题可以根据选择方式和问题类型进行分类。

按选择方式分类:
  1. 0/1背包问题:每个物品最多选取一次。
  2. 完全背包问题:每个物品可以重复选择。
  3. 组合背包问题:背包中的物品顺序重要,选择时要考虑排列组合。
  4. 分组背包问题:多个背包,每个背包内的物品只能选择一个,需要遍历每个背包。
按问题类型分类:
  1. 最值问题:求最大值或最小值。
  2. 存在问题:是否存在某种组合满足条件。
  3. 组合问题:求所有满足条件的排列组合数。
综合分类:

通过将选择方式与问题类型结合,可以得到如下常见的背包问题类型:

  1. 0/1 背包最值问题
  2. 0/1 背包存在问题
  3. 0/1 背包组合问题
  4. 完全背包最值问题
  5. 完全背包存在问题
  6. 完全背包组合问题
  7. 分组背包最值问题
  8. 分组背包存在问题
  9. 分组背包组合问题

背包问题解题模板

解决背包问题的基本方法是动态规划。解题的核心是设置一个dp数组,记录每种状态下的最优解,然后通过遍历物品和背包容量来更新dp

基本解题思路:
  1. 二维动态规划:定义dp[i][j]表示从前i个物品中选择不超过重量j的最大价值。
  2. 一维动态规划:通过去掉物品的那一层,简化成dp[j]表示容量为j的背包能放下的最大价值。
模板代码:

以经典的0/1背包问题为例,最基础的二维动态规划代码如下:

int[][] dp = new int[n + 1][target + 1];
// 遍历物品
for (int i = 1; i <= n; i++) {
   
    // 遍历背包容量
    for (int j = 0; j <= target; j++) {
   
        if (j >= weight[i - 1]) {
   
            dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - weight[i - 1]] + value[i - 1]);
        } else {
   
            dp[i][j] = dp[i - 1][j];
        }
    }
}

一维动态规划的简化代码如下:

int[] dp = new int[target + 1];
for (int i = 0; i < n; i++) {
   
    for (int j = target; j >= weight[i]; j--) {
   
        dp[j] = Math.max(dp[j], dp[j - weight[i]] + value[i]);
    }
}
分类解题模板:

根据背包问题的分类,解题时可以选择合适的遍历顺序和状态转移方程:

  • 0/1背包:外循环物品,内循环容量(倒序)。
  • 完全背包:外循环物品,内循环容量(正序)。
  • 组合背包:外循环容量,内循环物品(正序)。
  • 分组背包:三重循环,外层遍历背包,内层根据题目要求选取合适的背包类型。

状态转移方程的写法也因问题类型不同而有所变化:

  • 最值问题dp[i] = max/min(dp[i], dp[i-nums]+1)
  • 存在问题dp[i] = dp[i] || dp[i-num]
  • 组合问题dp[i] += dp[i-num]
例题解析

通过几个例题来具体说明如何应用背包问题的分类与模板:

  1. 最后一块石头的重量 II1049. 最后一块石头的重量 II)—— 01背包的最值问题。
  2. 零钱兑换322. 零钱兑换)—— 完全背包的最值问题。
  3. 分割等和子集416. 分割等和子集)—— 01背包的存在问题。

注意:
1、背包容量target和物品nums的类型可能是数,也可能是字符串
2、target可能题目已经给出(显式),也可能是需要我们从题目的信息中挖掘出来(非显式)(常见的非显式target比如sum/2等)
3、选取方式有常见的一下几种:每个元素选一次/每个元素选多次/选元素进行排列组合
那么对应的背包问题就是下面我们要讲的背包分类

背包问题解题模板(实践中记忆)

首先先了解一下原始背包问题的解题思路和代码:
最开始的背包问题是二维动态规划

import java.util.ArrayList;
import java.util.List;

public class KnapsackProblem {
   

    public static void knapsack() {
   
        List<Integer> weight = new ArrayList<>(List.of(1, 3, 4)); 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

blaizeer

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值