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:
- The length of the given array is positive and will not exceed 20.
- The sum of elements in the given array will not exceed 1000.
- 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
類似題目:
參考資料:
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