給定一個由正整數組成且不存在重複數字的數組,找出和爲給定目標正整數的組合的個數。
示例:
nums = [1, 2, 3]
target = 4
所有可能的組合爲:
(1, 1, 1, 1)
(1, 1, 2)
(1, 2, 1)
(1, 3)
(2, 1, 1)
(2, 2)
(3, 1)
請注意,順序不同的序列被視作不同的組合。
因此輸出爲 7。
進階:
如果給定的數組中含有負數會怎麼樣?
問題會產生什麼變化?
我們需要在題目中添加什麼限制來允許負數的出現?
思考:
首先考慮遞歸關係。怎樣的組合的
target
與那些比小的數的組合的target
?
所以我們知道這
target
是數組中數字的總和。想象一下,我們只需要一個數字來達到目標,這個數字可以是陣列中的任何一個,對嗎?所以#的組合target
,comb[target] = sum(comb[target - nums[i]]), where 0 <= i < nums.length, and target >= nums[i]
。
在給出的示例中,我們實際上可以找到4個組合的#與3(4 - 1),2(4 - 2)和1(4 - 3)的組合。結果,
comb[4] = comb[4-1] + comb[4-2] + comb[4-3] = comb[3] + comb[2] + comb[1]
。
然後考慮基本情況。因爲如果目標是0,只有一種方法可以獲得零,即使用0,我們可以設置
comb[0] = 1
。
編輯:問題是目標是一個正整數,讓我覺得它不清楚以上述方式。由於
target == 0
只在前一次調用中發生了target = nums [i],我們知道這是這種情況下唯一的組合,所以我們返回1。
現在我們可以提出至少一個遞歸解決方案。
public int combinationSum4(int[] nums, int target) {
if (target == 0) {
return 1;
}
int res = 0;
for (int i = 0; i < nums.length; i++) {
if (target >= nums[i]) {
res += combinationSum4(nums, target - nums[i]);
}
}
return res;
}
現在對於DP解決方案,我們只需要找出存儲中間結果的方法,以避免多次計算相同的組合和。我們可以使用數組來保存這些結果,並在計算之前檢查是否已有結果。我們可以用-1填充數組以指示結果尚未計算。0不是一個好的選擇,因爲它意味着目標沒有組合總和。
private int[] dp;
public int combinationSum4(int[] nums, int target) {
dp = new int[target + 1];
Arrays.fill(dp, -1);
dp[0] = 1;
return helper(nums, target);
}
private int helper(int[] nums, int target) {
if (dp[target] != -1) {
return dp[target];
}
int res = 0;
for (int i = 0; i < nums.length; i++) {
if (target >= nums[i]) {
res += helper(nums, target - nums[i]);
}
}
dp[target] = res;
return res;
}
編輯:以上解決方案是自上而下的。自下而上的怎麼樣?
public int combinationSum4(int[] nums, int target) {
int[] comb = new int[target + 1];
comb[0] = 1;
for (int i = 1; i < comb.length; i++) {
for (int j = 0; j < nums.length; j++) {
if (i - nums[j] >= 0) {
comb[i] += comb[i - nums[j]];
}
}
}
return comb[target];
}