题目链接:
本题是爬楼梯的又一变式。
分析样例可知,每次选择的都可以是 \(\rm nums\) 中的任一个数,而最后选择完毕的数之和等于 \(\rm target\).
可以认为我们每次从 \(\rm nums\) 中选一个数作为往上爬的台阶数,问爬 \(\rm target\) 个台阶有多少种方案。
因此爬楼梯那个题可以认为是本题条件下 \(\rm nums = [1,2]\) 的一个特殊情况 ,因为每次只能爬 \(1\) 个或 \(2\) 个台阶。
实现一、记忆化搜索
定义 \(\rm dfs(i)\) 为爬 \(i\) 个台阶的方案数。考虑最后一步爬了 \(\rm x=nums[j]\) 个台阶,那么问题变成爬 \(i-x\) 个台阶的方案数,即 \(\rm dfs(i-x)\)。
所以有 \(\rm dfs(i)=\sum\limits_{j=0}^{n-1}dfs(i-nums[j])\)
(仅在 \(\rm nums[j] \leqslant i\) 时成立)
递归边界为 \(dfs(0)=1\),只有当不选取任何元素时,元素之和才为 \(0\),因此只有 \(1\) 种方案。或这样理解:爬 \(0\) 个台阶的方案数为 \(1\)。也可以这样理解:我们从 \(\rm target\) 往下爬,刚好爬到底部(递归边界)此时就找到了一个合法的方案,返回 \(1\)。
递归入口为 \(\rm dfs(target)\),也就是答案。
爬楼梯那个题的递推表示也可以由此来推出,即 \(\rm dfs(i)=dfs(i-1)+dfs(i-2)\)。
class Solution {
public:
int combinationSum4(vector<int>& nums, int target) {
int n = nums.size();
vector<int> memo(target + 1, -1);
function<int(int)> dfs = [&] (int i) -> int {
if (i == 0) return 1;
int &res = memo[i];
if (res != -1) return res;
res = 0;
for (auto x : nums) {
if (x <= i) res += dfs(i - x);
}
return res;
};
return dfs(target);
}
};
实现二、递推
注意这里需要开到 \(\rm unsigned\) \(\rm long\) \(\rm long\) 才可以。
class Solution {
public:
int combinationSum4(vector<int>& nums, int target) {
int n = nums.size();
vector<unsigned long long> f(target + 1);
f[0] = 1;
for (int i = 1; i <= target; i++) {
for (auto x : nums) {
if (x <= i) f[i] += f[i - x];
}
}
return f[target];
}
};
标签:台阶,target,nums,int,dfs,IV,rm,377,总和
From: https://www.cnblogs.com/pangyou3s/p/18136993