乐趣区

关于程序员:数据结构与算法动态规划学习笔记背包问题

「数据结构与算法」动静布局学习笔记:背包问题

定义

给定一个背包容量 target ,再给定一个数组 nums(物品) ,是否按肯定形式选取 nums 中的元素失去 target

留神:

  1. 背包容量 target 和物品 nums 的类型可能是数,也可能是字符串
  2. target 可能题目曾经给出(显式),也可能是须要咱们从题目的信息中开掘进去(非显式)(常见的非显式 target 比方 sum/ 2 等)
  3. 选取形式有常见的一下几种:每个元素选一次 / 每个元素选屡次 / 选元素进行排列组合

分类:

常见的背包类型次要有以下几种:

  1. 0/ 1 背包问题:每个元素最多选取一次
  2. 齐全背包问题:每个元素能够反复抉择
  3. 组合背包问题:背包中的物品要思考程序
  4. 分组背包问题:不止一个背包,须要遍历每个背包

而每个背包问题要求的也是不同的,依照所求问题分类,又能够分为以下几种:

  1. 最值问题:要求最大值 / 最小值
  2. 存在问题:是否存在…………,满足…………
  3. 组合问题:求所有满足……的排列组合

模板

二维

// 0- 1 背包问题母代码(二维)
private int bags() {
    // 各个物品的分量
    int[] weight = new int[] {1, 3, 4};
    // 对应的价值
    int[] value = new int[] {15, 20, 30};
    // 背包最大能放下多重的物品
    int bagSize = 4;
    // 二维数组:状态定义:dp[i][j]示意从 0 - i 个物品中抉择不超过 j 分量的物品的最大价值
    int[][] dp = new int[weight.length][bagSize + 1];
    // 初始化: 第一列都是 0,第一行示意只选取 0 号物品最大价值
    for (int j = bagSize; j >= weight[0]; j--) {dp[0][j] = dp[0][j - weight[0]] + value[0];
    }
    for (int i = 1; i < weight.size(); i++) {// 遍历物品(第 0 个物品曾经初始化)
        for (int j = 0; j <= bagSize; j++) {
            // 遍历背包容量
            if (j < weight[i]) {
                // 背包容量曾经不足以拿第 i 个物品了
                // 最大价值就是拿第 i - 1 个物品的最大价值
                dp[i][j] = dp[i - 1][j];
            } else {
                // 背包容量足够拿第 i 个物品,可拿可不拿:// 拿了最大价值是前 i - 1 个物品扣除第 i 个物品的分量的最大价值,加上 i 个物品的价值
                // 不拿就是前 i - 1 个物品的最大价值
                // 两者进行比拟取较大的
                dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
            }
        }
    }
    return dp[weight.size - 1][bagSize];
}

二维优化至一维

// 0- 1 背包问题母代码(一维)
private int bags() {
    // 各个物品的分量
    int[] weight = new int[] {1, 3, 4};
    // 对应的价值
    int[] value = new int[] {15, 20, 30};
    // 背包最大能放下多重的物品
    int bagSize = 4;
    // 一维数组:状态定义:dp[i]示意抉择不超过 i 分量的物品的最大价值
    int[] dp = new int[bagSize + 1];
    for (int i = 0; i < weight.size(); i++) {
        // 遍历物品
        for (int j = bagSize; j >= weight[i]; j--) {
            // 遍历背包容量(肯定要逆序)// 不取或者取第 i 个
            dp[j] = Math.max(dp[j], dp[j - weight[i]] + value[i]);
        }
    }
    return dp[bagSize];
}

分类模板

背包问题大体的解题模板是两层循环,别离 遍历物品 nums 背包容量 target ,而后写转移方程。
依据 背包的分类 咱们 确定物品和容量遍历的先后顺序 ,依据 问题的分类 咱们 确定状态转移方程的写法

首先是 背包分类 的模板:

  1. 0/ 1 背包:外循环 nums ,内循环 target target 倒序且 target>=nums[i]
  2. 齐全背包:外循环 nums ,内循环 target target 正序且 target>=nums[i]
  3. 组合背包:外循环 target ,内循环 nums target 正序且 target>=nums[i]
  4. 分组背包:这个比拟非凡,须要三重循环:外循环背包 bags,外部两层循环依据题目的要求转化为 1、2、3 三种背包类型的模板

而后是 问题分类 的模板:

  1. 最值问题: dp[i] = max/min(dp[i], dp[i-nums]+1) dp[i] = max/min(dp[i], dp[i-num]+nums)
  2. 存在问题: dp[i] = dp[i]||dp[i-num]
  3. 组合问题: dp[i] += dp[i-num]
退出移动版