1. 程式人生 > >[LeetCode] 494. Target Sum

[LeetCode] 494. Target Sum

題目

You are given a list of non-negative integers, a1, a2, …, an, and a target, S. Now you have 2 symbols + and -. For each integer, you should choose one from + and - as its new symbol.

Find out how many ways to assign symbols to make sum of integers equal to target S.

Example 1:

Input: nums is [1, 1, 1, 1, 1], S is 3. 
Output: 5
Explanation: 

-1+1+1+1+1 = 3
+1-1+1+1+1 = 3
+1+1-1+1+1 = 3
+1+1+1-1+1 = 3
+1+1+1+1-1 = 3

There are 5 ways to assign symbols to make the sum of nums be target 3.

Note:

  1. The length of the given array is positive and will not exceed 20.
  2. The sum of elements in the given array will not exceed 1000.
  3. Your output answer is guaranteed to be fitted in a 32-bit integer.

題目大意

取 陣列 nums 中 的每個元素 或 其負數,求所有和為S 的組合數。

思路

該問題轉換為 Subset Sum 問題,從而使用 0-1 揹包的方法來求解。

可以將這組數看成兩部分,P 和 N,其中 P 使用正號,N 使用負號,有以下推導:

                  sum(P) - sum(N) = target
sum(P) + sum(N) + sum(P) - sum(N) = target + sum(P) + sum(N)
                       2 * sum(P) = target + sum(nums)

因此只要找到一個子集,令它們都取正號,並且和等於 (target + sum(nums))/2,就證明存在解。

具體解法:

揹包問題,動態規劃:

狀態int dp[i][j] :陣列中,前i個數的subSum為j的組合個數 。

初始化狀態:dp[0][0] =0, 前0個數的subSum 定為 0 組合個數為1。

狀態轉移方程: 近似揹包問題的狀態轉移方程。

            for(int j = 0;j <= target;j++){
                if(j<nums[i-1])
                    dp[i][j] = dp[i-1][j];
                else
                    dp[i][j] = dp[i-1][j] + dp[i-1][j-nums[i-1]];
            }

code:

class Solution {
    public int findTargetSumWays(int[] nums, int S) {
        int sum = 0;
        for(int num:nums)
            sum += num;
        if(sum < S || (sum+S)%2==1)
            return 0;
        int target = (sum+S)/2;
        int[][] dp = new int[nums.length+1][target+1];
        System.out.println(target);
        dp[0][0] = 1;
        for(int i = 1;i <= nums.length;i++)
            for(int j = 0;j <= target;j++){
                if(j<nums[i-1])
                    dp[i][j] = dp[i-1][j];
                else
                    dp[i][j] = dp[i-1][j] + dp[i-1][j-nums[i-1]];
            }
        return dp[nums.length][target];
    }
}

由於dp[i][j] 只用到 dp[i-1][j],所以 可以使用一維。

class Solution {
    public int findTargetSumWays(int[] nums, int S) {
        int sum = 0;
        for(int num:nums)
            sum += num;
        if(sum < S || (sum+S)%2==1)
            return 0;
        int target = (sum+S)/2;
        int[] dp = new int[target+1];
        dp[0] = 1;
        for(int num:nums)
            for(int j = target;j >= num;j--)
                dp[j] = dp[j] + dp[j-num];
        return dp[target];
    }
}