题目
题目来源:力扣(LeetCode)
给你一个由 不同 整数组成的数组 nums ,和一个目标整数 target 。请你从 nums 中找出并返回总和为 target 的元素组合的个数。
题目数据保证答案符合 32 位整数范围。
示例 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
思路分析
动态规划
本题题目描述说是求组合,但又说是可以元素相同顺序不同的组合算两个组合,其实就是求排列!
弄清什么是组合,什么是排列很重要。
组合不强调顺序,(1,5)和(5,1)是同一个组合。
排列强调顺序,(1,5)和(5,1)是两个不同的排列。
本题求的是排列总和,而且仅仅是求排列总和的个数,并不是把所有的排列都列出来
1、状态定义
dp[i]:表示凑成目标正整数为 i 的排列个数
2、确定递推公式
假设目标正整数为 i ,当 1 ≤ i ≤ target 时,如果存在一种排列,其中的元素之和等于 i ,则该排列的最后一个元素一定是数组 nums 中的一个元素。假设该排列的最后一个元素是 num,则一定有 num ≤ i ,对于元素之和等于 i - num 的每一种排列,最后添加 num 之后即可得到一个元素之和等于 i 的排列,因此,在计算 dp[i] 时,应该计算所有的 dp[i - num] 之和。
因此,dp[i] (考虑 num 的排列总和) 就是所有的dp[i- num] (不考虑 num)相加。
所以递推公式:dp[i] += dp[i - num]
3、状态初始化
dp[0]=1:只有当不选取任何元素时,元素之和才为 0,因此只有 1 种方案。dp[0]要初始化为1,这样递归其他dp[i]的时候才会有数值基础。其实dp[0] = 1是没有意义的,仅仅是为了推导递推公式。
非0下标的dp[i]应该初始化为0,这样才不会影响dp[i]累加所有的dp[i - nums[j]] 。
4、确定遍历顺序
如果求组合数就是外层for循环遍历物品,内层for遍历背包。
如果求排列数就是外层for遍历背包,内层for循环遍历物品。
本题最终遍历顺序:target(背包)放在外循环,将nums(物品)放在内循环,内循环从前到后遍历。
代码实现
/**
* @param {number[]} nums
* @param {number} target
* @return {number}
*/
var combinationSum4 = function (nums, target) {
const dp = new Array(target + 1).fill(0);
// dp[0]=1 只有当不选取任何元素时,元素之和才为 0,因此只有 1 种方案。
dp[0] = 1;
// target(背包)放在外循环,将nums(物品)放在内循环,内循环从前到后遍历
for (let i = 1; i <= target; i++) {
for (const num of nums) {
if (num <= i) {
dp[i] += dp[i - num];
}
}
}
return dp[target];
};
参考阅读 https://leetcode-cn.com/problems/combination-sum-iv/solution/zu-he-zong-he-iv-by-leetcode-solution-q8zv/ https://leetcode-cn.com/problems/combination-sum-iv/solution/dai-ma-sui-xiang-lu-377-zu-he-zong-he-iv-pj9s/ https://leetcode-cn.com/problems/combination-sum-iv/solution/yi-pian-wen-zhang-chi-tou-bei-bao-wen-ti-qkgr/