package com.freetymekiyan.algorithms.level.medium;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import java.util.Arrays;
/**
* Given an integer array with all positive numbers and no duplicates, find the number of possible combinations that add
* up to a positive integer target.
* <p>
* Example:
* <p>
* nums = [1, 2, 3]
* target = 4
* <p>
* The possible combination ways are:
* (1, 1, 1, 1)
* (1, 1, 2)
* (1, 2, 1)
* (1, 3)
* (2, 1, 1)
* (2, 2)
* (3, 1)
* <p>
* Note that different sequences are counted as different combinations.
* Therefore the output is 7.
* <p>
* Follow up:
* What if negative numbers are allowed in the given array?
* How does it change the problem?
* What limitation we need to add to the question to allow negative numbers?
* <p>
* Company Tags: Google, Snapchat, Facebook
* Tags: Dynamic Programming
* Similar Problems: (M) Combination Sum
* <p>
* Answer to follow up:
* 1. The recurrence relation still works. But base case breaks.
* 2. Each number is only used one time. Think about [1, -1], 1.
*/
public class CombinationSum4 {
private CombinationSum4 c;
private int[] dp;
/**
* DP. Bottom-up.
* State: S[i] means the # of combinations that can reach sum i.
* Recurrent relation:
* S[i] = sum(S[i - nums[j]]), where 0 <= j < nums.length, and target >= nums[j].
* Base case:
* S[0] = 1. Think about [1], 1, where S[1] = S[1 - 1] = S[0] = 1.
*/
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) { // Array's not sorted. Need to check each number.
comb[i] += comb[i - nums[j]];
}
}
}
return comb[target];
}
/**
* DP. Top-down.
*/
public int combinationSum4TopDown(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 (target == 0) {
return 1;
}
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;
}
@Before
public void setUp() {
c = new CombinationSum4();
}
@Test
public void testExamples() {
int[] nums = {1, 2, 3};
int target = 4;
Assert.assertEquals(7, c.combinationSum4(nums, target));
target = 6;
Assert.assertEquals(24, c.combinationSum4(nums, target));
}
@After
public void tearDown() {
c = null;
}
}