目录
力扣377. 组合总和 Ⅳ(似包非包)
解析代码
力扣377. 组合总和 Ⅳ(似包非包)
377. 组合总和 Ⅳ
难度 中等
给你一个由 不同 整数组成的数组 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 <= nums.length <= 200
1 <= nums[i] <= 1000
nums
中的所有元素 互不相同1 <= target <= 1000
进阶:如果给定的数组中含有负数会发生什么?问题会产生何种变化?如果允许负数出现,需要向题目中添加哪些限制条件?
class Solution {
public:
int combinationSum4(vector<int>& nums, int target) {
}
};
解析代码
需要知道的是背包问题本质上求的是组合数问题,而这一道题虽然题目是组合总数,但求的是排列数问题(排列是无序的,组合是有序的),题目感觉不太严谨。(虽然不能用背包问题解决,但代码类似背包问题,所以称为似包非包)用常规的 dp 思想来解决这道题。
这道题的状态表示就是根据拆分出相同子问题的方式,抽象出来一个状态表示: 当在求 target 这个数一共有几种排列方式的时候,对于最后一个位置,如果我们拿出数组中的一个数 x ,接下来就是去找 target - x 一共有多少种排列方式。 因此可以抽象出来一个状态表示:
dp[i] 表示:总和为 i 的时候,一共有多少种排列方案
状态转移方程:
线性 dp 状态转移方程分析方式,一般都是根据最后一步的状况,可以选择数组中的任意一个数 nums[j] ,其中 0 <= j <= n - 1 。
当 nums[j] <= target 的时候,此时的排列数等于先找到 target - nums[j] 的方案数,然后在每t一个方案后面加上一个数字 nums[j] 即可。 因为有很多个 j 符合情况,因此我们的状态转移方程为: dp[i] += dp[target - nums[j]] ,其中 0 <= j <= n - 1 。
初始化: 和为 0 的时候,可以什么都不选,空集一种方案,因此 dp[0] = 1 。
填表顺序: 根据状态转移方程易得从左往右。
返回值: 根据状态表示,返回 dp[target]。
class Solution {
public:
int combinationSum4(vector<int>& nums, int target) {
// dp[i] 表示:总和为 i 的时候,一共有多少种排列方案
vector<double> dp(target + 1, 0); // double防溢出
dp[0] = 1;
int sz = nums.size();
for(int i = 1; i <= target; ++i)
{
for(int j = 0; j < sz; ++j)
{
if(i >= nums[j])
dp[i] += dp[i - nums[j]];
}
}
return dp[target];
}
};