8/23背包

0-1背包:一维的话,dp[j]代表j的容量所能装的最大价值;

	  for(let i=0;i<nums.length;i++) {
        for(let j =bagSize;j>=nums[i];--j) {
            arr[j] += arr[j-nums[i]]
        }
    }
    	  for(let i=0;i<nums.length;i++) {
        for(let j =bagSize;j>=nums[i];--j) {
            arr[j] = Math.max(arr[j],arr[j-nums[i]]+value[i])
        }
    }
    

目标和

	var findTargetSumWays = function(nums, target) {
    let sum = nums.reduce((a,b)=>a+b)
    if(Math.abs(target)>sum) return 0
    if((sum+target)%2===1) return 0
    let bagSize = parseInt((target+sum)/2)
    const arr = new Array(bagSize+1).fill(0)
    arr[0]=1
    for(let i=0;i<nums.length;i++) {
        for(let j =bagSize;j>=nums[i];--j) {
            arr[j] += arr[j-nums[i]]
        }
    }
    return arr[bagSize]
};
var findTargetSumWays = function(nums, target) {
    let res = 0;
    const findAll = (cur, len) => {
        if(len === nums.length && cur === target) { // 所有整数都被串联,和恰好是target
            res++;
            return;
        }
        if(len >= nums.length) return; // 所有整数都被串联,和不是target
        // 向数组中的每个整数前添加 '+' 或 '-'
        findAll(cur + nums[len], len + 1)
        findAll(cur - nums[len], len + 1)
    }
    findAll(0, 0);
    return res;
};


分割等和子集

var canPartition = function(nums) {
    let sum = nums.reduce((a,b)=>a+b)
    if(sum%2===1) return false
    let bagSize = parseInt(sum/2)
        if(Math.max(nums)>bagSize) return false
    const dp = new Array(bagSize+1).fill(false)
    dp[0]=true
    for(let i=0;i<nums.length;i++) {
        for(let j=bagSize;j>=nums[i];--j){
            if(dp[bagSize]) return true
            dp[j] = dp[j-nums[i]] || dp[j]
        }
    }
    return dp[bagSize]
};



for(int i = 0; i < nums.size(); i++) {
    for(int j = target; j >= nums[i]; j--) { // 每一个元素一定是不可重复放入,所以从大到小遍历
        dp[j] = max(dp[j], dp[j - nums[i]] + nums[i]);
    }
}
var canPartition = function(nums) {
    let sum = nums.reduce((a,b)=>a+b)
    if(sum%2===1) return false
    let bagSize = parseInt(sum/2)
        if(Math.max(nums)>bagSize) return false
    const dp = new Array(bagSize+1).fill(0)
    for(let i=0;i<nums.length;i++) {
        for(let j=bagSize;j>=nums[i];--j){
              dp[j] = Math.max(dp[j], dp[j - nums[i]] + nums[i]);
        }
    }
    return dp[bagSize]===bagSize
};

上面两题,都是从数组中挑选数来求和,区别在于第一个需要求所有的方法,第二个只需要有就可以,外层循环是挑选物品。内层遍历所有容量的可能性;倒序是因为从前到后的话,对于某个物品会重复计算,从后到前不会(计算就是依赖前面的,所以肯定是从往前才不会有影响),每次计算都会考虑带不带之前的物品。

确定dp数组以及下标的含义
01背包中,dp[j] 表示: 容量为j的背包,所背的物品价值最大可以为dp[j]。

本题中每一个元素的数值既是重量,也是价值。

套到本题,dp[j]表示 背包总容量(所能装的总重量)是j,放进物品后,背的最大重量为dp[j]。

那么如果背包容量为target, dp[target]就是装满 背包之后的重量,所以 当 dp[target] == target 的时候,背包就装满了。

有录友可能想,那还有装不满的时候?

拿输入数组 [1, 5, 11, 5],距离, dp[7] 只能等于 6,因为 只能放进 1 和 5。

而dp[6] 就可以等于6了,放进1 和 5,那么dp[6] == 6,说明背包装满了。

确定递推公式
01背包的递推公式为:dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);

本题,相当于背包里放入数值,那么物品i的重量是nums[i],其价值也是nums[i]。

所以递推公式:dp[j] = max(dp[j], dp[j - nums[i]] + nums[i]);

dp数组如何初始化
在01背包,一维dp如何初始化,已经讲过,

从dp[j]的定义来看,首先dp[0]一定是0。

如果题目给的价值都是正整数那么非0下标都初始化为0就可以了,如果题目给的价值有负数,那么非0下标就要初始化为负无穷。

这样才能让dp数组在递推的过程中取得最大的价值,而不是被初始值覆盖了。

一和零

var findMaxForm = function(strs, m, n) {
    const dp = Array.from(new Array(m+1),()=>new Array(n+1).fill(0))
   for(let i=0;i<strs.length;i++) {
       let one =0,zero=0
       for(let char of strs[i]) {
           if(char === '0') zero++
           if(char === '1') one++
       }
       for(let x=m;x>=zero;x--) {
           for(let y=n;y>=one;y--) {
               dp[x][y] = Math.max(dp[x][y],dp[x-zero][y-one]+1)
           }
       }
   }
   return dp[m][n]
};

目标和

var findTargetSumWays = function(nums, target) {
    const sum = nums.reduce((a,b)=>a+b)
    if((sum+target)%2===1) return 0
    if(Math.abs(target)>sum) return 0
    let bagSize = (sum+target)/2
    const dp =new Array(bagSize+1).fill(0)
    dp[0] =1
    for(let i=0;i<nums.length;i++) {
        for(let j=bagSize;j>=nums[i];--j) {
            dp[j] = dp[j]+dp[j-nums[i]]
        }
    }
    return dp[bagSize]
};

最后一块石头的重量 II

var lastStoneWeightII = function(stones) {
    let sum = stones.reduce((a,b)=>a+b)
    bagSize= parseInt(sum/2)
    const dp = new Array(bagSize+1).fill(0)
    for(let i=0;i<stones.length;i++) {
        for(let j = bagSize;j>=stones[i];j--) {
            dp[j] = Math.max(dp[j],dp[j-stones[i]]+stones[i])
        }
    }
    
    return  sum - 2*dp[bagSize]
};

完全背包

每个物品可以选择多次,不用怕重复,所以前往后
零钱兑换

var coinChange = function(coins, amount) {
    const dp = new Array(amount+1)
    if(amount===0) return 0
    for(let i=0;i<dp.length;i++) {
        dp[i]=10001
    }
    dp[0]=0
    for(let i=0;i<coins.length;i++) {
        for(let j=coins[i];j<=amount;j++) {
            if (dp[j - coins[i]] != 10001) { // 如果dp[j - coins[i]]是初始值则跳过
                    dp[j] = Math.min(dp[j - coins[i]] + 1, dp[j]);
                }
        }
    }
    return dp[amount]===10001?-1:dp[amount]
};

完全平方数

var numSquares = function(n) {
    const arr = []
    for(let i=1;i<=n;i++) {
        if(parseInt(Math.sqrt(i))*parseInt(Math.sqrt(i))===i )
        arr.push(i)
    }
    const dp = new Array(n+1).fill(10001)
    dp[0]=0
    for(let i=0;i<arr.length;i++) {
        for(let j=arr[i];j<=n;j++) {
            dp[j] = Math.min(dp[j],dp[j-arr[i]]+1)
        }
    }
    return dp[n]
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

SuperHaker~

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

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

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

打赏作者

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

抵扣说明:

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

余额充值