echo

任生命穿梭 时间的角落

0%

组合总和Ⅳ

377. 组合总和 Ⅳ

给你一个由 不同 整数组成的数组 nums ,和一个目标整数 target 。请你从 nums 中找出并返回总和为 target 的元素组合的个数。

题目数据保证答案符合 32 位整数范围。

示例 1:

1
2
3
4
5
6
7
8
9
10
11
12
输入: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:

1
2
输入:nums = [9], target = 3
输出:0

提示:

  • 1 <= nums.length <= 200
  • 1 <= nums[i] <= 1000
  • nums 中的所有元素 互不相同
  • 1 <= target <= 1000

进阶:如果给定的数组中含有负数会发生什么?问题会产生何种变化?如果允许负数出现,需要向题目中添加哪些限制条件?

方法一:回溯(超时)

我们可以画出搜索路径,很自然写出如下回溯代码

image-20210428094002017

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Solution {
public int combinationSum4(int[] nums, int target) {
int n = nums.length;
int res = 0;
for(int start = 0; start < n; ++start){
res += backtrack(nums, target, nums[start]);
}
return res;
}

public int backtrack(int[] nums, int target, int cur){
int n = nums.length;
if(cur > target){
return 0;
}
if(target == cur){
return 1;
}
int res = 0;
//每次进入下一层都是从 1 开始
for(int i = 0; i < n; ++i){
res += backtrack(nums, target, cur + nums[i]);
}
return res;
}
}

方法二:动态规划

用 dp[x] 选取的元素之和等于 x 的方案数,目标是求 dp[target]。动态规划的边界是 dp[0] = 1。只有当不选取任何元素时,元素之和才为 0 ,因此只有 1 种方案。

1 <= i <= target时,如果存在一种排列,其元素之和等于 i,则该排列最后一个元素一定是数组 nums 种的元素。假设该排列最后一个元素是 num,则一定有 num <= i,对于元素之和等于 i - num 的每一种排列,在最后添加 num 之后可以得到一个元素之和等于 i 的排列,因此在计算 dp[i] 时,应该计算所有的 dp[i - num] 之和。

1 <= i <= target时,得到如下转移方程:
$$
dp[i] = sum(dp[i - num]), num \leq i
$$
初始状态 dp[0] = 1。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Solution {
public int combinationSum4(int[] nums, int target) {
int[] dp = new int[target + 1];
dp[0] = 1;
for(int i = 1; i < dp.length; i++){
for(int num : nums){
if(num <= i){
dp[i] += dp[i - num];
}
}
}
return dp[target];
}
}
  • 时间复杂度O(target * n),target 是 目标值,n 是 数组 nums 的长度。
  • 空间复杂度O(target)