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

[leetcode-494-Target Sum]

tco 都是 而是 明顯 isa 運行 ble oss original

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.

思路:

分析:深度優先搜索,嘗試每次添加+或者-,

當當前cnt為nums數組的大小的時候,判斷sum與S是否相等,

如果相等就result++。sum為當前cnt步數情況下的前面所有部分的總和。

參考:

https://www.liuchuo.net/archives/3098

int result;
     int findTargetSumWays(vector<int>& nums, int S) {
         dfs(0, 0, nums, S);
         return result;
     }
     void dfs(int sum, int cnt, vector<int>& nums, int S) {
         if (cnt == nums.size()) {
             if (sum == S)
                 result
++; return; } dfs(sum + nums[cnt], cnt + 1, nums, S); dfs(sum - nums[cnt], cnt + 1, nums, S); }

如下是動態規劃版本介紹,參考:https://discuss.leetcode.com/topic/76243/java-15-ms-c-3-ms-o-ns-iterative-dp-solution-using-subset-sum-with-explanation

The recursive solution is very slow, because its runtime is exponential

The original problem statement is equivalent to:
Find a subset of nums that need to be positive, and the rest of them negative, such that the sum is equal to target

Let P be the positive subset and N be the negative subset
For example:
Given nums = [1, 2, 3, 4, 5] and target = 3 then one possible solution is +1-2+3-4+5 = 3
Here positive subset is P = [1, 3, 5] and negative subset is N = [2, 4]

Then let‘s see how this can be converted to a subset sum problem:

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

So the original problem has been converted to a subset sum problem as follows:
Find a subset P of nums such that sum(P) = (target + sum(nums)) / 2

Note that the above formula has proved that target + sum(nums) must be even
We can use that fact to quickly identify inputs that do not have a solution (Thanks to @BrunoDeNadaiSarnaglia for the suggestion)
For detailed explanation on how to solve subset sum problem, you may refer to Partition Equal Subset Sum

Here is Java solution (15 ms)

    public int findTargetSumWays(int[] nums, int s) {
        int sum = 0;
        for (int n : nums)
            sum += n;
        return sum < s || (s + sum) % 2 > 0 ? 0 : subsetSum(nums, (s + sum) >>> 1); 
    }   

    public int subsetSum(int[] nums, int s) {
        int[] dp = new int[s + 1]; 
        dp[0] = 1;
        for (int n : nums)
            for (int i = s; i >= n; i--)
                dp[i] += dp[i - n]; 
        return dp[s];
    } 

Here is C++ solution (3 ms)

class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int s) {
        int sum = accumulate(nums.begin(), nums.end(), 0);
        return sum < s || (s + sum) & 1 ? 0 : subsetSum(nums, (s + sum) >> 1); 
    }   

    int subsetSum(vector<int>& nums, int s) {
        int dp[s + 1] = { 0 };
        dp[0] = 1;
        for (int n : nums)
            for (int i = s; i >= n; i--)
                dp[i] += dp[i - n];
        return dp[s];
    }
};

Dynamic Programming方法

參考:https://zhangyuzhu13.github.io/2017/02/13/LeetCode%E4%B9%8B494.%20Target%20Sum%E6%80%9D%E8%B7%AF/

要想到DP方法需要再分析一下題目了,乍一看似乎看不出有求最優解的痕跡。我所熟悉的使用DP場景都是需要求最優解,找最優子結構的。這個問題不明顯。但可以往0-1背包問題上想一想,每個數字為正或為負,同時增一倍,則變為了,每個數字不選,或選2倍。這就靠到0-1背包上了。則基數就不再是0,而是nums數組中所有數字之和為基數,在此基礎上進行選2倍或不選,目標數字S也相應變為S+Sum。依靠數學公式推論為:設最後選擇為正的之和為in,為負的之和為out,則有公式:
in - out = S
in + out = sum
推出:2*in = S + sum
則我們需要的就是把目標改為S+sum,把每個數字改為原來的2倍,從中選擇數字,使之和為S+sum。
因此,DP解之。代碼如下:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class Solution { public int findTargetSumWays(int[] nums, int S) { int sum = 0; for(int i = 0;i < nums.length;i++){ sum += nums[i]; nums[i] *= 2; } if(sum < S ) return 0; int target = sum + S; int[] dp = new int[target+1]; dp[0] = 1; for(int i = 0;i < nums.length; i++){ for(int j = target;j >= 0;j--){ if(j >= nums[i]){ dp[j] += dp[j-nums[i]]; } } } return dp[target]; } }

然後運行時間就。。到了20ms,擊敗80%+,DP大法好。。

 

[leetcode-494-Target Sum]