组合总和 IV
问题描述
给定一个由 不同 正整数组成的数组 nums
,和一个目标整数 target
。请从 nums
中找出并返回总和为 target
的元素组合的个数。数组中的数字可以在一次排列中出现任意次,但是顺序不同的序列被视作不同的组合。
示例
示例 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
的元素组合的个数。可以将问题转化为:背包容量为目标整数 target
,物品为数组 nums
。
因此,我们可以定义一个动态规划数组 dp
,其中 dp[i]
表示总和为 i
的组合数。
状态定义
定义一个一维动态规划数组 dp
,其中 dp[i]
表示总和为 i
的组合数。
状态转移方程
对于每一个数字 nums[j]
,我们有两种选择:使用该数字或者不使用该数字(抽象为选和不选)。因此状态转移方程为:
dp[i] += dp[i - nums[j]];
初始化
我们需要对动态规划数组进行初始化。初始时,总和为 0 的组合数有 1 种方法,其余为 0。
遍历顺序
注意:这里要求的是排序数,即2+2+1和1+2+2和2+1+2各视为三种不同组合 (即要考虑顺序),那么需要先背包物品,再遍历物品,这样求得就是物品的排序数了
理解:
- 先遍历物品的话,就先拿着物品1,再遍历每一个背包容量,再拿物品2,再遍历每一个背包容量,那么背包中就只会出现(1,2)这样的组合;不会把(2,1)重复算成一种情况。
- 如果先遍历背包的话,就先拿着背包1,再遍历每一个物品(物品1,物品2…),再拿背包2,再遍历物品(物品1,物品2…),那么背包中就只会出现(1,2,1,2…),那么对于(1,2)来说,就会出现(1,2)和(2,1)都各自算成一种情况了。
Java解题
public class Solution {
public int combinationSum4(int[] nums, int target) {
// 创建一个动态规划数组 dp,其中 dp[i] 表示目标数为 i 时的组合数
int[] dp = new int[target + 1];
// 目标数为 0 时,组合数为 1
dp[0] = 1;
// 遍历目标数从 1 到 target
for (int i = 1; i <= target; i++) {//先遍历背包
// 遍历 nums 数组
for (int j = 0; j < nums.length; j++) {//后遍历物品(则为物品的排序数)
// 如果当前目标数(背包容量)大于等于当前 nums[j](物品),则更新组合数
if (i >= nums[j]) {
dp[i] += dp[i - nums[j]]; // 注意这里dp数组的含义,[这里是背包容量的遍历下标]
}
}
}
// 返回目标数为 target 时的组合数
return dp[target];
}
}
总结
通过动态规划的思想,我们可以解决这个问题。首先初始化动态规划数组,然后根据状态转移方程进行状态转移,最终返回总和为 target
的组合数。