[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.

 

這道題給了我們一個數組,和一個目標值,讓給數組中每個數字加上正號或負號,然后求和要和目標值相等,求有多少中不同的情況。那么對於這種求多種情況的問題,博主最想到的方法使用遞歸來做。從第一個數字,調用遞歸函數,在遞歸函數中,分別對目標值進行加上當前數字調用遞歸,和減去當前數字調用遞歸,這樣會涵蓋所有情況,並且當所有數字遍歷完成后,若目標值為0了,則結果 res 自增1,參見代碼如下:

 

解法一:

class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int S) {
        int res = 0;
        helper(nums, S, 0, res);
        return res;
    }
    void helper(vector<int>& nums, long S, int start, int& res) {
        if (start >= nums.size()) {
            if (S == 0) ++res;
            return;
        }
        helper(nums, S - nums[start], start + 1, res);
        helper(nums, S + nums[start], start + 1, res);
    }
};

 

我們對上面的遞歸方法進行優化,使用 memo 數組來記錄中間值,這樣可以避免重復運算,參見代碼如下:

 

解法二:

class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int S) {
        vector<unordered_map<int, int>> memo(nums.size());
        return helper(nums, S, 0, memo);
    }
    int helper(vector<int>& nums, long sum, int start, vector<unordered_map<int, int>>& memo) {
        if (start == nums.size()) return sum == 0;
        if (memo[start].count(sum)) return memo[start][sum];
        int cnt1 = helper(nums, sum - nums[start], start + 1, memo);
        int cnt2 = helper(nums, sum + nums[start], start + 1, memo);
        return memo[start][sum] = cnt1 + cnt2;
    }
};

 

我們也可以使用迭代的方法來解,使用一個 dp 數組,其中 dp[i][j] 表示到第 i-1 個數字且和為j的情況總數,參見代碼如下:

 

解法三:

class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int S) {
        int n = nums.size();
        vector<unordered_map<int, int>> dp(n + 1);
        dp[0][0] = 1;
        for (int i = 0; i < n; ++i) {
            for (auto &a : dp[i]) {
                int sum = a.first, cnt = a.second;
                dp[i + 1][sum + nums[i]] += cnt;
                dp[i + 1][sum - nums[i]] += cnt;
            }
        }
        return dp[n][S];
    }
};

 

我們也可以對上面的方法進行空間上的優化,只用一個 HashMap,而不是用一個數組的哈希表,在遍歷數組中的每一個數字時,新建一個 HashMap,在遍歷原 HashMap 中的項時更新這個新建的 HashMap,最后把新建的 HashMap 整個賦值為原 HashMap,參見代碼如下:

 

解法四:

class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int S) {
        unordered_map<int, int> dp;
        dp[0] = 1;
        for (int num : nums) {
            unordered_map<int, int> t;
            for (auto a : dp) {
                int sum = a.first, cnt = a.second;
                t[sum + num] += cnt;
                t[sum - num] += cnt;
            }
            dp = t;
        }
        return dp[S];
    }
};

 

Github 同步地址:

https://github.com/grandyang/leetcode/issues/494

 

類似題目:

Expression Add Operators

 

參考資料:

https://leetcode.com/problems/target-sum/

https://leetcode.com/problems/target-sum/discuss/97371/Java-Short-DFS-Solution

https://leetcode.com/problems/target-sum/discuss/97369/Evolve-from-brute-force-to-dp

https://leetcode.com/problems/target-sum/discuss/97334/Java-(15-ms)-C%2B%2B-(3-ms)-O(ns)-iterative-DP-solution-using-subset-sum-with-explanation

 

LeetCode All in One 題目講解匯總(持續更新中...)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM