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.
 * <p>
 * Therefore the output is 7.
 * 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>
 * Tags: Dynamic Programming
 * Similar Problems: (M) Combination Sum
 */
public class CombinationSum4 {

    private CombinationSum4 c;

    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 (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;
    }

    public int combinationSum4BottomUp(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];
    }

    @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;
    }

}

@@ -599,23 +599,16 @@
 ations.%0A
- * %3Cp%3E%0A
  * There
@@ -629,16 +629,23 @@
 t is 7.%0A
+ * %3Cp%3E%0A
  * Follo
@@ -825,24 +825,68 @@
 ers?%0A * %3Cp%3E%0A
+ * Company Tags: Google, Snapchat, Facebook%0A
  * Tags: Dyn
@@ -949,127 +949,1039 @@
 m%0A *
-/%0Apublic class CombinationSum4 %7B%0A%0A    private CombinationSum4 c;%0A%0A    private int%5B%5D dp;%0A%0A    public int combinationSum4
+ %3Cp%3E%0A * Answer to follow up:%0A * 1. The recurrence relation still works. But base case breaks.%0A * 2. Each number is only used one time. Think about %5B1, -1%5D, 1.%0A */%0Apublic class CombinationSum4 %7B%0A%0A    private CombinationSum4 c;%0A%0A    private int%5B%5D dp;%0A%0A    /**%0A     * DP. Bottom-up.%0A     * Recurrence relation:%0A     * comb%5Btarget%5D = sum(comb%5Btarget - nums%5Bi%5D%5D), where 0 %3C= i %3C nums.length, and target %3E= nums%5Bi%5D.%0A     * Base case:%0A     * comb%5B0%5D = 1. Think about %5B1%5D, 1, where comb%5B1%5D = comb%5B1 - 1%5D = comb%5B0%5D = 1.%0A     */%0A    public int combinationSum4(int%5B%5D nums, int target) %7B%0A        int%5B%5D comb = new int%5Btarget + 1%5D;%0A        comb%5B0%5D = 1;%0A        for (int i = 1; i %3C comb.length; i++) %7B%0A            for (int j = 0; j %3C nums.length; j++) %7B%0A                if (i - nums%5Bj%5D %3E= 0) %7B // Array's not sorted. Need to check each number.%0A                    comb%5Bi%5D += comb%5Bi - nums%5Bj%5D%5D;%0A                %7D%0A            %7D%0A        %7D%0A        return comb%5Btarget%5D;%0A    %7D%0A%0A    /**%0A     * DP. Top-down.%0A     */%0A    public int combinationSum4TopDown
 (int
@@ -2552,404 +2552,8 @@
  %7D%0A%0A
-    public int combinationSum4BottomUp(int%5B%5D nums, int target) %7B%0A        int%5B%5D comb = new int%5Btarget + 1%5D;%0A        comb%5B0%5D = 1;%0A        for (int i = 1; i %3C comb.length; i++) %7B%0A            for (int j = 0; j %3C nums.length; j++) %7B%0A                if (i - nums%5Bj%5D %3E= 0) %7B%0A                    comb%5Bi%5D += comb%5Bi - nums%5Bj%5D%5D;%0A                %7D%0A            %7D%0A        %7D%0A        return comb%5Btarget%5D;%0A    %7D%0A%0A
     

update combination sum 4