组合总和 Ⅳ 图解题解
这道题到底在问什么
- 输入
- nums = [1,2,3], target = 6
- 输出
- 24
先想最直接的笨办法
记住一句:dp[t] 把「最后一步选谁」全枚举一遍,每个选择把对应 dp[t−num] 加进来。(动画第 3 步)
最优解:一步一步想明白
- 3记住一句:dp[t] 把「最后一步选谁」全枚举一遍,每个选择把对应 dp[t−num] 加进来。
- 4dp[0]=1先填边界:凑出和 0,只有"一个数都不选"这一种 → dp[0]=1。
- 5凑和 1:最后一个数选 1,剩下要凑 0 → 把 dp[0]=1 加进来。
- 61 条来路全加完:dp[1] = 1。
- 7凑和 2:最后一个数选 1,剩下要凑 1 → 把 dp[1]=1 加进来。
- 8凑和 2:最后一个数选 2,剩下要凑 0 → 把 dp[0]=1 加进来。
- 92 条来路全加完:dp[2] = 2。
- 10凑和 3:最后一个数选 1,剩下要凑 2 → 把 dp[2]=2 加进来。
- 11凑和 3:最后一个数选 2,剩下要凑 1 → 把 dp[1]=1 加进来。
- 12凑和 3:最后一个数选 3,剩下要凑 0 → 把 dp[0]=1 加进来。
- 133 条来路全加完:dp[3] = 4。
- 14凑和 4:最后一个数选 1,剩下要凑 3 → 把 dp[3]=4 加进来。
- 15凑和 4:最后一个数选 2,剩下要凑 2 → 把 dp[2]=2 加进来。
- 16凑和 4:最后一个数选 3,剩下要凑 1 → 把 dp[1]=1 加进来。
- 173 条来路全加完:dp[4] = 7。
- 18凑和 5:最后一个数选 1,剩下要凑 4 → 把 dp[4]=7 加进来。
- 19凑和 5:最后一个数选 2,剩下要凑 3 → 把 dp[3]=4 加进来。
- 20凑和 5:最后一个数选 3,剩下要凑 2 → 把 dp[2]=2 加进来。
- 213 条来路全加完:dp[5] = 13。
- 22凑和 6:最后一个数选 1,剩下要凑 5 → 把 dp[5]=13 加进来。
- 23凑和 6:最后一个数选 2,剩下要凑 4 → 把 dp[4]=7 加进来。
- 24凑和 6:最后一个数选 3,剩下要凑 3 → 把 dp[3]=4 加进来。
- 253 条来路全加完:dp[6] = 24。
- 26dp[6]=24dp[6]=24 就是凑出 6 的全部组合数。一维 DP 一遍扫完,O(target × nums) 搞定。
⚠️ 容易写错的地方
✗ 错:外层 num、内层 t(求组合的写法)
✓ 对:外层 t、内层 num
本题数顺序(排列),必须让同一数在不同位置重复计
✗ 错:忘了 dp[0] = 1
✓ 对:dp[0] 必须初始化为 1
它是空方案,所有递推的源头,漏了全表归零
✗ 错:结果用 int 存
✓ 对:C++/Java 用 long/无符号
target 大时排列数会溢出 int(本题官方明说会爆 int)
完整代码(Python / C++ / Java)
Python
class Solution:
def combinationSum4(self, nums, target):
dp = [0] * (target + 1)
dp[0] = 1 # 凑出 0 有空方案这一种
for t in range(1, target + 1): # 外层:总和 t
for num in nums: # 内层:最后取哪个 num
if num <= t:
dp[t] += dp[t - num] # 累加 dp[t-num]
return dp[target]C++
class Solution {
public:
int combinationSum4(vector<int>& nums, int target) {
vector<unsigned long long> dp(target + 1, 0);
dp[0] = 1; // 空方案
for (int t = 1; t <= target; t++) { // 外层:总和
for (int num : nums) { // 内层:末尾数
if (num <= t) dp[t] += dp[t - num];
}
}
return (int) dp[target];
}
};Java
class Solution {
public int combinationSum4(int[] nums, int target) {
long[] dp = new long[target + 1];
dp[0] = 1; // 空方案
for (int t = 1; t <= target; t++) { // 外层:总和
for (int num : nums) { // 内层:末尾数
if (num <= t) dp[t] += dp[t - num];
}
}
return (int) dp[target];
}
}复杂度
时间复杂度
O(target × n)
每个总和 t 都扫一遍 nums(n 个数),共 target 个总和
空间复杂度
O(target)
一维 dp 数组,长度 target+1
看不够?换成动画再走一遍
上面的推演每一步都对应一帧动画。点开交互动画版,能一步步看着 组合总和 Ⅳ 的数据怎么变、指针怎么走,还能切 Python / Java / C++ 跟着练。
面试官可能追问
题目叫「组合」,为什么算的是排列?+
因为示例把 (1,1,2) 和 (2,1,1) 当成不同方案——顺序不同即不同。所以本质是排列计数,转移时外层枚举总和、内层枚举末尾数。
如果 nums 含负数会怎样?+
会出问题:负数能让总和反复增减,可能凑出无限长的序列,dp 无界。官方追问就是这个——需先限定序列长度或额外约束才可解。
和完全背包求方案数有什么联系?+
完全相同的骨架。求组合数时外层物品、内层容量;求排列数(本题)时外层容量、内层物品。一行 dp、循环顺序定性质。
想听吴师兄把这道题讲给你听?
文字版和动画都随便看。开通图解算法年卡,可以听吴师兄声线把 组合总和 Ⅳ 一步步讲透(全站 0 节语音精讲,持续扩充), 卡住的地方还有 AI 私教小欧就着动画帮你拆到懂。
本平台为独立第三方培训机构,与华为技术有限公司无任何关联;课程的服务内容与权益以购买协议为准,学习效果因个人情况而异。「华为 OD」「华为可信」等仅为对岗位与考试方向的客观描述,相关商标归各自权利人所有。