class Solution:
def combinationSum4(self, nums, target):
dp = [0] * (target + 1)
dp[0] = 1
for total in range(1, target + 1):
for num in nums:
if total - num >= 0:
dp[total] += dp[total - num]
return dp[target]
class Solution {
public:
int combinationSum4(vector<int>& nums, int target) {
vector<int> dp(target + 1, 0);
dp[0] = 1;
for (int total = 1; total <= target; ++total) {
for (int num : nums) {
if (total - num >= 0) {
dp[total] += dp[total - num];
}
}
}
return dp[target];
}
};
class Solution {
public int combinationSum4(int[] nums, int target) {
int[] dp = new int[target + 1];
dp[0] = 1;
for (int total = 1; total <= target; total++) {
for (int num : nums) {
if (total - num >= 0) {
dp[total] += dp[total - num];
}
}
}
return dp[target];
}
}
var combinationSum4 = function(nums, target) {
const dp = new Array(target + 1).fill(0);
dp[0] = 1;
for (let total = 1; total <= target; total++) {
for (let num of nums) {
if (total - num >= 0) {
dp[total] += dp[total - num];
}
}
}
return dp[target];
};
Given an array of distinct positive integers nums
and a positive integer target
, you are asked to find the number of possible combinations that add up to target
. Each number in nums
can be used an unlimited number of times in each combination, and the order of numbers in the combination matters (i.e., [1,2]
and [2,1]
are considered different combinations).
nums
are positive and distinct.target
.
At first glance, this problem looks similar to the classic "coin change" or "subset sum" problems. One might think of generating all possible combinations recursively and counting those that sum to target
. However, since the order of numbers matters, permutations (not just combinations) must be considered. This increases the number of possible solutions significantly.
A naive brute-force approach would try every possible sequence of numbers up to the target, which quickly becomes infeasible for larger inputs. To optimize, we can use dynamic programming to store and reuse results for subproblems (i.e., the number of ways to reach each intermediate sum).
The key insight is to recognize that for each total from 1
to target
, we can build up the answer by considering all numbers in nums
and summing the number of ways to make total - num
.
We'll use a bottom-up dynamic programming approach, where we build up the solution for all values from 0
to target
. Here’s how we do it:
dp
of size target + 1
, where dp[i]
will store the number of ways to reach the sum i
.dp[0] = 1
because there is exactly one way to reach sum 0 (by choosing nothing).1
to target
:num
in nums
:total - num ≥ 0
, add dp[total - num]
to dp[total]
.dp[target]
as the final answer.We use an array because we want fast, indexed access to the number of ways to reach each sum. The outer loop ensures we build up from the smallest subproblems, and the inner loop tries every number as the last element in a combination.
Let's walk through an example with nums = [1, 2, 3]
and target = 4
.
dp = [1, 0, 0, 0, 0]
(dp[0]=1
)dp[1] += dp[0] → dp[1]=1
dp = [1, 1, 0, 0, 0]
dp[2] += dp[1] → 1
dp[2] += dp[0] → 2
dp = [1, 1, 2, 0, 0]
dp[3] += dp[2] → 2
dp[3] += dp[1] → 3
dp[3] += dp[0] → 4
dp = [1, 1, 2, 4, 0]
dp[4] += dp[3] → 4
dp[4] += dp[2] → 6
dp[4] += dp[1] → 7
dp = [1, 1, 2, 4, 7]
There are 7 different ordered combinations to sum up to 4: [1,1,1,1], [1,1,2], [1,2,1], [2,1,1], [2,2], [1,3], [3,1].
nums
and T is the target
, since each recursive call can branch N ways up to target depth.nums
and T is target
. For each total from 1 to target, we iterate through all numbers.target + 1
.This is a huge improvement over the brute-force method, making it feasible for larger inputs.
To solve Combination Sum IV, we use dynamic programming to efficiently count the number of ordered combinations that sum to the target. By building up from smaller subproblems and storing intermediate results, we avoid redundant work and achieve a fast, elegant solution. The key insight is treating the problem as a permutation problem (since order matters) and using a DP array to record the number of ways to reach each sum.