菜鸟前端学算法qaq
最近感觉解题越来越吃力了,很多看题解都想不明白为什么.......,一晚上只能看一两道题
xdm有什么好办法吗
背包问题技巧
一般背包问题要注意遍历顺序(谁在外层循环)
以及内层循环的遍历顺序
两个问题
01背包
-
二维dp数组,先遍历物品,还是先遍历背包容量,都可以.内层循环从0开始,还是从最后开始递减,都可以
-
一维dp数组,
内层循环必须倒序开始
,防止多次放入同一物品
一维dp数组中,递推公式dp[j] += dp[j - nums[i]]
,如果正序遍历,那么这里 的dp[j - nums[i]]
是已经计算过放入该物品后的值,dp[j] + dp[j - nums[i]]
就已经是放入这件物品了,dp[j - nums[i]]
又放了这件物品。相当于放入了两次物品,背包容量越大,放的次数就越多。 -
一维dp数组,
外层循环必须是物品
因为如果外层循环是背包容量
的话,**背包容量**
只能倒序遍历,见原因2
例如此时的dp数组初始化
是[0, 0, 0, 0, 0]
,当背包容量为4时
,我装了一个物品重量为2
,我还剩2
的背包容量,但是此时dp[2]
还是0,所以结果就是我背包容量为4时,只装了一个物品
,还剩2个背包容量,浪费了。
完全背包
- 二维dp数组,先物品还是先背包容量,都可以。但是内层必须正序遍历。因为
可能会多次放入同一物品
,打印二维dp数组看一下应该不难理解。 - 一维dp数组,分为求
组合数
和求排列数
组合数
,物品外循环
,背包容量内循环
,且正序遍历
排列数
,背包容量外循环
,物品内循环
, 且正序遍历
518. 零钱兑换 II
给你一个整数数组 coins
表示不同面额的硬币,另给一个整数 amount
表示总金额。
请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额,返回 0
。
假设每一种面额的硬币有无限个。
题目数据保证结果符合 32 位带符号整数。
示例 1:
输入:amount = 5, coins = [1, 2, 5]
输出:4
解释:有四种方式可以凑成总金额:
5=5
5=2+2+1
5=2+1+1+1
5=1+1+1+1+1
示例 2:
输入:amount = 3, coins = [2]
输出:0
解释:只用面额 2 的硬币不能凑成总金额 3 。
示例 3:
输入:amount = 10, coins = [10]
输出:1
这是一个完全背包
问题,物品可以无限次放进去
JavaScript
var change = function(amount, coins) {
let dp = Array(amount + 1).fill(0) // 初始化为0
dp[0] = 1 // 组合成0元的方法,有且只有一种,什么也不拿
for(let i = 1; i <= coins.length; i++) {
for(let j = 1; j <= amount; j++) {
if(j >= coins[i - 1]) { // 背包容量大于物品重量
dp[j] += dp[j - coins[i - 1]]
}
// 否则就不动,dp[j] 等于0 到 (i - 1)物品,j重量的最大价值
}
}
return dp[amount]
}
377. 组合总和 Ⅳ
给你一个由 不同 整数组成的数组 nums ,和一个目标整数 target 。请你从 nums 中找出并返回总和为 target 的元素组合的个数。
题目数据保证答案符合 32 位整数范围。
来源:力扣(LeetCode)
示例 1:
输入:nums = [1,2,3], target = 4
输出:7
解释:
所有可能的组合为:
(1, 1, 1, 1)
(1, 1, 2)
(1, 2, 1)
(1, 3)
(2, 1, 1)
(2, 2)
(3, 1)
请注意,顺序不同的序列被视作不同的组合。
示例 2:
输入:nums = [9], target = 3
输出:0
这道题卡了我好久。。。脑子死活转不过来,哭了
思路:
这是一道完全背包
问题,元素可以重复放入。
根据示例,每个值不同的位置可以构成不同的组合,所以求的是数组中和等于target的排列数
JavaScript
var combinationSum4 = function (nums, target) {
let dp = Array(target + 1).fill(0) // 背包容量为i时,所有物品构成i有几种方式,先全部置为0
dp[0] = 1 // 用所有的物品组合成0的组合数,有且只有一种,也就是什么物品都不拿
for(let i = 1; i <= target; i++) { // 所有物品组合成i(背包重量)有几种方式
for(let j = 1; j <= nums.length; j++) { // 必须正序,因为是完全背包
if(i >= nums[j - 1]) { 如果当前背包容量能够放下该物品,
// 例如背包重量为4,物品重量为1,那么组合数就是dp[3] (1可以插入到每个位置)
// 然后继续下一个物品, dp[2] + 2(2可以插入到(组成2的排列中)每一个位置)
dp[i] += dp[i - nums[j - 1]]
}
}
}
return dp[target]
}