題目網址:https://leetcode.cn/problems/target-sum/
題意:給一 array nums 和整數 target, 在 nums 中每個整前添加 + 或 -, 然後串聯整個 nums 形成一個表達式:
- e.g.
nums = [2, 1] 可以在 2 前添加 +, 在 1 前添加 -, 形成表達式 +2-1
求運算結果等於 target 的不同表達式數目。

Solution 1:
想法:利用暴力法, 遞迴下去做(速度極慢)
class Solution { public: int findTargetSumWays(vector<int>& nums, int target) { const int sum = accumulate(nums.begin(), nums.end(), 0);
if (target > abs(sum) || target < -abs(sum)) { return 0; }
int res = 0; dfs(nums, 0, nums.size(), target, res);
return res; }
void dfs(const vector<int>& nums, const int i, const int n, const int target, int& res){ if (i == n) { if (target == 0) { ++res; } return; }
dfs(nums, i + 1, n, target + nums[i], res); dfs(nums, i + 1, n, target - nums[i], res); } };
|
- time:$O(2^n)$ ➔
nums 中每個數有兩種選擇
- space:$O(n)$ ➔ 取決於遞迴的深度, 遞迴最大深度為
n
Solution 2:
想法:利用 DP, 可把這題看做是背包問題, 每一個 nums[i] 都有取正 or 取負兩種選擇
1. 定義狀態:
dp[i][j]:nums[1:i] 湊出 j 的方法數
2. 得到狀態轉移方程:
dp[i][j] = dp[i - 1][j - nums[i]] + dp[i - 1][j + nums[i]]
3. 初始化:
- 首先, 會在
nums 前先加上一個 0, 代表什麼元素都沒有的狀態
dp[0][0] = 1:代表沒有元素時, 湊出 0 的方案為 1 種
4. 注意事項:
本題 dp[i][j] 的 j 之範圍為 [-abs(sum), abs(sum)], 但 index 不允許負數, 因此統一加上 offset = abs(sum), 將其 mapping 到範圍 [0, 2 * abs(sum)]

class Solution { public: int findTargetSumWays(vector<int>& nums, int target) { const int sum = accumulate(nums.begin(), nums.end(), 0);
if (target > abs(sum) || target < -abs(sum)) { return 0; }
nums.emplace(nums.begin(), 0);
vector<vector<int>> dp(n + 1, vector<int>(2 * abs(sum) + 1, 0));
const int offset = abs(sum), n = nums.size(); dp[0][0 + offset] = 1;
for (int i = 1; i <= n; ++i) { for (int j = -abs(sum); j <= abs(sum); ++j) { if (j - nums[i] >= -abs(sum) && j - nums[i] <= abs(sum)) { dp[i][j + offset] += dp[i - 1][j - nums[i] + offset]; }
if (j + nums[i] >= -abs(sum) && j + nums[i] <= abs(sum)) { dp[i][j + offset] += dp[i - 1][j + nums[i] + offset]; } } }
return dp[n][target + offset]; } };
|
- time:$O(sum \cdot n)$ ➔ for loop, 其中
sum 為 nums 元素之和
- space:$O(sum \cdot n)$ ➔
dp
Solution 3:
想法:Solution 2 的改進, 實際上 dp[i][j + offset] 只會用到上一列 dp[i - 1][X], 故不需要開到 $O(sum \cdot n)$ 的空間, 只需 $O(sum)$ 的空間即可
class Solution { public: int findTargetSumWays(vector<int>& nums, int target) { const int sum = accumulate(nums.begin(), nums.end(), 0);
if (target > abs(sum) || target < -abs(sum)) { return 0; }
nums.emplace(nums.begin(), 0);
vector<int> dp(2 * abs(sum) + 1, 0);
const int offset = abs(sum), n = nums.size(); dp[0 + offset] = 1;
for (int i = 1; i <= n; ++i) { vector<int> nextRow(2 * abs(sum) + 1, 0);
for (int j = -abs(sum); j <= abs(sum); ++j) { if (j - nums[i] >= -abs(sum) && j - nums[i] <= abs(sum)) { nextRow[j + offset] += dp[j - nums[i] + offset]; }
if (j + nums[i] >= -abs(sum) && j + nums[i] <= abs(sum)) { nextRow[j + offset] += dp[j + nums[i] + offset]; } }
dp = move(nextRow); }
return dp[target + offset]; } };
|
- time:$O(sum \cdot n)$ ➔ for loop, 其中
sum 為 nums 元素之和
- space:$O(sum)$ ➔
dp, nextRow 皆只需 $O(2 \cdot sum + 1)$