This commit is contained in:
youngyangyang04
2020-12-25 09:27:53 +08:00
parent f53e2b25b0
commit 194abf83f1
10 changed files with 162 additions and 68 deletions

View File

@@ -1,5 +1,41 @@
# 思路
和之前个回溯法的各个总和串起来一波,本题用回溯稳稳的超时
本题题目描述说是求组合,但又说是可以元素相同顺序不同的组合算两个组合,其实就是求排列!
弄清什么是组合,什么是排列很重要。
大家在学习回溯算法系列的时候,一定做过这两道题目[回溯算法39.组合总和](https://mp.weixin.qq.com/s/FLg8G6EjVcxBjwCbzpACPw)和[回溯算法40.组合总和II](https://mp.weixin.qq.com/s/_1zPYk70NvHsdY8UWVGXmQ)
大家会感觉很像,但其本质是本题求的是排列总和,而且仅仅是求排列总和的个数,并不是把所有的排列都列出来。
如果本题要把排列都列出来的话,只能使用回溯算法爆搜。
* 确定dp数组以及下标的含义
dp[i]: 凑成目标正整数为i的组合个数为dp[i]
* 确定递推公式
dp[i]考虑nums[j])可以由 dp[i - nums[j]]不考虑nums[j] 推导出来。
因为只要得到nums[j]排列个数dp[i - nums[j]]就是dp[i]的一部分。
所以递归公式: dp[i] += dp[i - nums[j]];
* dp数组如何初始化
因为递推公式dp[i] += dp[i - nums[j]]的缘故dp[0]要初始化为1这样递归其他dp[i]的时候才会有数值基础。
非0下标的dp[i]初始化为0这样才不会影响dp[i]累加所有的dp[i-nums[j]]
* 确定遍历顺序
个数可以不限使用,这是一个完全背包,且得到的集合是排列(需要考虑元素之间的顺序)。
所以将target放在外循环将nums放在内循环内循环从前到后遍历。
C++代码如下:
```
class Solution {
@@ -19,28 +55,9 @@ public:
};
```
C++测试用例有超过两个树相加超过int的数据所以需要在if里加上dp[i] < INT_MAX - dp[i - num]。
C++测试用例有超过两个树相加超过int的数据
超限的情况
但java就不用考虑这个限制我理解java里的int也是四个字节吧也有可能leetcode后台对不同语言的测试数据不一样
一些题解会直接用ull usigned long long
java 也是四个字节理论上没有差别可能后台java和C++测试用例不同bug.....
```
class Solution {
public:
int combinationSum4(vector<int>& nums, int target) {
vector<int> dp(target + 1, 0);
dp[0] = 1;
for (int i = 0; i <= target; i++) {
for (int num : nums) {
if (i - num >= 0 && dp[i] < INT_MAX - dp[i - num]) {
dp[i] += dp[i - num];
}
}
}
return dp[target];
}
};
```