一、最后一块石头的重量2
该题的思路是把一堆石头分为重量尽量相同的两堆,这样两堆相撞后剩下的石头最小,这个又可以装换位01背包的问题
五部曲
dp[j],大小为j的背包,最多可以容纳多少重量的石头- 递推公式
dp[j] = Math.max(dp[j], dp[j - stones[i]] + stones[i]) - 初始化,0
- 遍历顺序,物品在外层遍历,背包在内层遍历,且应该为倒序遍历
- 举例推导
/**
* @param {number[]} stones
* @return {number}
*/
var lastStoneWeightII = function(stones) {
let sum = stones.reduce((sum, cur) => sum + cur)
let target = Math.floor(sum / 2)
let dp = new Array(target + 1).fill(0)
for(let i = 0; i < stones.length;i++) {
for(let j = target; j >= stones[i];j--) {
dp[j] = Math.max(dp[j], dp[j - stones[i]] + stones[i])
}
}
let targetSum = dp[target]
return sum - dp[target] - dp[target]
};
二、目标和
left为加法的总和,right为减法的总和 left + right = sum left - right = target left = (target + sum) / 2
回溯法
/**
* @param {number[]} nums
* @param {number} target
* @return {number}
*/
var findTargetSumWays = function(nums, target) {
let sum = nums.reduce((sum, cur) => sum + cur)
if (target > sum) {
return 0
}
if ((target + sum) % 2) {
return 0
}
let bagSize = (target + sum) / 2
nums.sort((a, b) => a - b)
let result = []
function backtracking(sum, startIndex) {
if (bagSize === sum) {
result.push([...result])
}
for (let i = startIndex; i < nums.length && sum + nums[i] <= bagSize; i++) {
result.push(nums[i])
backtracking(sum + nums[i], i + 1)
result.pop()
}
}
backtracking(0, 0)
return result.length
};
动态规划
上面left,加法的总和即为背包的大小,问题转换为装满容量为left的背包,有几种方法
五部曲
d[j],装满j有这么多的方法- 递推公式,
dp[j] += dp[j - nums[i]],组合类的问题都是这种公式 dp[0] = 1- 遍历顺序,外层是数组,内层是背包倒序
- 举例
var findTargetSumWays = function(nums, target) {
let sum = nums.reduce((sum, cur) => sum + cur)
if (Math.abs(target) > sum) {
return 0
}
if ((target + sum) % 2) {
return 0
}
let bagSize = (target + sum) / 2
let 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 - nums[i]]
}
}
return dp[bagSize]
};
三、一和零
两个维度的背包,m和n,分别表示最多有m个0和最多有n个1,典型的01背包问题
五部曲
dp[i][j]表示最多有i个0和j个1的最大子集大小- 递推公式,
dp[i][j]可以由上个strs的字符串推导而来,字符串有zeroNum个0,oneNum个1,dp[i][j] = Math.max(dp[i][j], dp[i-zeroNum][j-onenNum] + 1) - dp初始化为0
- 遍历顺序,外层遍历为物品,内层为m和n的背包倒序遍历
- 举例推导
/**
* @param {string[]} strs
* @param {number} m
* @param {number} n
* @return {number}
*/
var findMaxForm = function(strs, m, n) {
let dp = new Array(m+1).fill(0).map(_ => new Array(n+1).fill(0))
for(let str of strs) {
let zeroNum = 0
let oneNum = 0
for(let i=0; i < str.length;i++) {
if(str[i] === '0') {
zeroNum++
}
if(str[i] === '1') {
oneNum++
}
}
for(let i = m; i >= zeroNum;i--) {
for(let j = n; j >= oneNum; j--) {
dp[i][j] = Math.max(dp[i][j], dp[i-zeroNum][j-oneNum] + 1)
}
}
}
return dp[m][n]
};