给你一个由 不同 整数组成的数组 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) 请注意,顺序不同的序列被视作不同的组合。
提示:
1 <= nums.length <= 200
1 <= nums[i] <= 1000
nums
中的所有元素 互不相同1 <= target <= 1000
动态规划
目标是找出总和为target的元素组合个数,整个计算过程是累加的过程。因此,想得到target,一定会在计算过程中得到小于target的某个中间数,那么问题就从找到和为target的元素组合个数变成了找到target-x的组合个数。
实际上,由于数组中最小数为1,且在任何位置都有可能出现1,也就是说这里的x可能在任何时刻为1。所以dp[target]最差依赖于dp[target - 1]。
因此,设计dp[ i ]表示和为 i 时的组合个数。最终dp[target]即为所求。如上所述,我们需要计算dp[0] 到 dp[target]的每一个值。
状态转移方程:
dp[i] += dp[i - num]
以[1,2,3],target = 4为例,计算过程如下:
- 首先dp[0] = 1,因为数组中最小值为1,所以只有一个数都不取的时候,和是0,只有一种组合。(对所有情况都成立,边界情况)
- 计算dp[1]
遍历数组,num = 1,i = 1。dp[1] += dp[1 - 1],得到dp[1] = 1;
而当num >= 2 > i = 1时,和已经大于当前target = i了,无需计算。 - 计算dp[2]
遍历数组,num = 1 ,i = 2。dp[2] += dp[2 - 1],得到dp[2] = 1;
继续遍历,num = 2 ,i = 2。dp[2] += dp[2 - 2],得到dp[2] = 2;
接下来num > i,跳出循环。 - 计算dp[3]
num = 1 ,i = 3。dp[3] += dp[3 - 1],得到dp[3] = 2;
num = 2 ,i = 3。dp[3] += dp[3 - 2],得到dp[3] = 3;
num = 3 ,i = 3。dp[3] += dp[3 - 3],得到dp[3] = 4; - 计算dp[4]
num = 1 ,i = 4。dp[4] += dp[4 - 1],得到dp[4] = 4;
num = 2 ,i = 4。dp[4] += dp[4 - 2],得到dp[4] = 6;
num = 3 ,i = 4。dp[4] += dp[4 - 3],得到dp[4] = 7; - 得到结果dp[target] = dp[4]。
在这个过程中,外层循环是dp[target],内层循环是遍历数组。在每次计算target时都遍历数组的每一个数,因而是考虑了不同排列组合的,例如[1,2,1]和[1,1,2]在遍历中是两种情况,前者dp[1] -> dp[3] -> dp[4],后者dp[1] -> dp[2] -> dp[4]。
代码:
class Solution {
public:
int combinationSum4(vector<int>& nums, int target) {
int n = nums.size();
vector<unsigned> dp(target + 1,0);
dp[0] = 1;
for(int i = 1;i <= target;++i){
for(int num : nums){
if(num <= i) dp[i] += dp[i - num];
}
}
return dp[target];
}
};