01背包与分割等和⼦集与目标和【动态规划】

在不断增加的背包中选取物品,以达到当前背包当前能够选取的物品的最大值,分割等和子集道理和背包一样。

let w = 10,
    W = [2, 3, 5, 7],
    V = [3, 4, 6, 18];
let res = 0
const wl = W.length;

let dp = new Array(wl + 1).fill(0).map((e) => new Array(w + 1).fill(0)); 
//初始化第一个:什么都不选 ,是对于第一个物品都选不了的情况。 第二个初始化:是背包装不下了

for (let i = 1; i <= wl; i++) {
    const t = i - 1;
    for (let j = 1; j <= w; j++) {
        if (j - W[t] < 0) {
            dp[i][j] = dp[t][j];
        } else {
            dp[i][j] = Math.max(dp[t][j - W[t]] + V[t], dp[t][j]);
        }
    }
}
res = dp[wl][w]
console.log(dp);


console.log(res);
//下面这个一样

let dp = new Array(w + 1).fill(0).map((e) => new Array(wl + 1).fill(0)); 

for (let i = 1; i <= w; i++) {
  for (let j = 1; j <= wl; j++) {
    const t = j - 1;
    if (i - W[t] < 0) {
      dp[i][j] = dp[i][t];
    } else {
      dp[i][j] = Math.max(dp[i - W[t]][t] + V[t], dp[i][t]);
    }
  }
}
console.log(dp);



在这里插入以图片描述

降维处理

let dp = new Array(w + 1).fill(0) //  dp【j】中的每个值代表在j容量的时候的最大值,容量为0的时候必然最大值为零

for (let i = 1; i <= w; i++) { //现在是以重量为第一层 
    for (let j = 0; j < wl; j++) { //如果这样处理的话 那么始终会导致重复选取  就比如说 当i=2 => dp【2】 =3
        //  那么 i=4 的时候会 还是选从背包中选取第一个 导致dp【4】=6  故就不能以重量来作为第一层 
        if (i - W[j] >= 0) {
            dp[i] = Math.max(dp[i - W[j]] + V[j], dp[i - 1], dp[i])
        }
    }
}
考虑重量作为第二层,因为dp【j】中的每个值代表在j容量的时候的最大值,j又越来越大
,对于每一个物品,依然考虑选与不选,那么没法判断以前选过没有,故考虑在背包越来越小的时候我们去找最大值。

let dp = new Array(w + 1).fill(0) //  dp【j】中的每个值代表在j容量的时候的最大值,容量为0的时候必然最大值为零

for (let i = 0; i < wl; i++) {
    for (let j = w; j > 0; j--) {
        if (j >= W[i]) {//这样写好理解些 
            dp[j] = Math.max(dp[j - W[i]] + V[i], dp[j])//相当于滚动数组 又相当于选与不选 
        }
    }
}
console.log(dp);

分割等和⼦集 道理和背包问题一样

let W = [1, 3, 5, 1, 62, 6, 2, 6, 26, 2, 6, 2, 62, 6, 2, 1];

function aa() {
    let w = W.reduce((a, b) => a + b) / 2

    if (Math.floor(w) - w < 0) return false
    const wl = W.length;
    let dp = new Array(wl + 1).fill(0).map((e) => new Array(w + 1).fill(0));
    for (let i = 1; i <= wl; i++) {
        const t = i - 1;
        for (let j = 1; j <= w; j++) {
             if (j - W[t] <0) {
                dp[i][j] =  dp[t][j]
            }else{
                dp[i][j]=Math.max(dp[t][j - W[t]] + W[t], dp[t][j]);
            }
            if (dp[i][j] === w) {
                return true
            }
        }
    }
    console.log(dp);
    return w === dp[wl][w]
}
console.log(aa());

目标和来源于leetcode,放在这里是提醒: dp[0][0] = 1;
dp[i][j] = dp[i - 1][j];
if (j >= num) {
dp[i][j] += dp[i - 1][j - num];
}
这种情况,在每一步不是求极值,而是求和


let W = [1, 1, 1, 1, 2];
s = 2
var findTargetSumWays = function (nums, target) {

    
    let sum = 0;
    for (const num of nums) {
        sum += num;
    }
    const diff = sum - target;
    if (diff < 0 || diff % 2 !== 0) {
        return 0;
    }
    const n = nums.length,
        neg = diff / 2; 
        console.log(neg);
    const dp = new Array(n + 1).fill(0).map(() => new Array(neg + 1).fill(0));
    dp[0][0] = 1;
    for (let i = 1; i <= n; i++) {
        const num = nums[i - 1];
        for (let j = 0; j <= neg; j++) {
            dp[i][j] = dp[i - 1][j];                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    
            if (j >= num) {
                dp[i][j] += dp[i - 1][j - num];                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     
            }
        }
    }
    console.log(dp); 
    return dp
};


findTargetSumWays(W, s)

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值