算法练习day36

102 阅读3分钟

一、01背包

1. 二维dp数组01背包

有n件物品和1个最多能背重量w的背包,第i件物品的重量是weight[i],得到的价值为value[i],每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大

暴力解法

每个物品的状态是取或者不取,通过回溯搜索出所有的情况,那时间复杂度就是o(2^n), 指数级别,所有需要动态规划来优化

动态规划五部曲

  1. 确定dp数组,dp[i][j],表示从0-i的物品里任意取,放进容量为j的背包,价值总和最大是多少
  2. 确定递推公式,dp[i][j] = Math.max(dp[i-1][j], dp[i-1][j-weight[i]] + value[i])
  3. dp数组初始化
    1. 当j为0, dp[i][0] = 0
    2. 当i为0,当j < weight[0] dp[0][j] = 0,容量太小放不进去
    3. 当i为0,当j >= weight[0] dp[0][j] = value[0],容量可以放进去0的物品
  4. 遍历顺序

let inputArr = [
    [6, 1],
    [2, 2, 3, 1, 5, 2],
    [2, 3, 1, 5, 4, 3]
];
function fun(inputArr) {
    let [M, N] = inputArr[0]
    let weight = inputArr[1]
    let value = inputArr[2]
    let dp = new Array(M).fill(0).map(_ => new Array(N + 1).fill(0))
    for (let j = weight[0]; j <= N; j++) {
        dp[0][j] = value[0]
    }
    for (let i = 1; i < M; i++) {
        for (let j = 0; j <= N; j++) {
            if (j < weight[i]) {
                dp[i][j] = dp[i - 1][j]
            } else {
                dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i])
            }
        }
    }
    return dp[M - 1][N]
}

console.log(fun(inputArr))

2. 一维dp数组(滚动数组)

五部曲

  1. dp数组的含义,容量为j的背包,所背物品的最大价值
  2. 确定递推公式,dp[j] = Math.max(dp[j], dp[j - weight[i]]) + value[i]
  3. dp初始化 dp[0] = 0
  4. 遍历顺序,必须是从后往前,对于二维dp,dp[i][j]是根据dp[i-1][j]计算而来,一维dp,倒序遍历是为了保证左边的值依旧是上一层的状态,不会重复计算
  5. 举例

let inputArr = [
    [6, 1],
    [2, 2, 3, 1, 5, 2],
    [2, 3, 1, 5, 4, 3]
];
function fun(inputArr) {
    let [M, N] = inputArr[0]
    let weight = inputArr[1]
    let value = inputArr[2]
    let dp = new Array(N + 1).fill(0)
    dp[0] = 0
    for (let i = 0; i < M; i++) {
        for(let j = N; j >= weight[i]; j--) {
            dp[j] = Math.max(dp[j], dp[j-weight[i]] + value[i])
        }
    }
    return dp[N]
}

console.log(fun(inputArr))

二、分割等和子集

可以转换为01背包问题,背包的体积为 sum / 2,背包能够放的最大和等于背包大小的时候,为true

五部曲

  1. dp[j],容量为j的背包,能够容纳的最大价值,j为目标和,dp[j]是最大和
  2. 递归公式: dp[j] = Math.max(dp[j], dp[j - nums[i]] + nums[i])
  3. dp初始化 dp[0] = 0
  4. 遍历顺序
  5. 举例
/**
 * @param {number[]} nums
 * @return {boolean}
 */
var canPartition = function(nums) {
    let sum = nums.reduce((sum, cur) => sum + cur)
    if(sum % 2 === 1) {
        return false
    }
    let target = sum / 2
    let dp = new Array(target + 1).fill(0)
    for(let i = 0; i < nums.length;i++) {
        for(let j = target; j >= nums[i]; j--) {
            dp[j] = Math.max(dp[j], dp[j - nums[i]] + nums[i])
        }
    }
    return dp[target] === target
};