xlua筆記 1.C#載入lua檔案
題目連結:
給你一個整數陣列 nums
和一個整數 target
。
向陣列中的每個整數前新增 '+'
或 '-'
,然後串聯起所有整數,可以構造一個 表示式 :
-
例如,
nums = [2, 1]
,可以在2
之前新增'+'
,在1
之前新增'-'
,然後串聯起來得到表示式"+2-1"
。
返回可以通過上述方法構造的、運算結果等於 target
的不同 表示式 的數目。
示例 1:
輸入:nums = [1,1,1,1,1], target = 3
輸出:5
解釋:一共有 5 種方法讓最終目標和為 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
+1 + 1 + 1 + 1 - 1 = 3
示例 2:
輸入:nums = [1], target = 1
輸出:1
提示:
-
1 <= nums.length <= 20
-
0 <= nums[i] <= 1000
-
0 <= sum(nums[i]) <= 1000
-
-1000 <= target <= 1000
解題思路
假設符號為+
於是題目就可以轉換為:裝滿容量為left
的揹包,最多有幾種方法。這可以用01揹包來解。
首先,我們要兩種情況需要先排除:
-
當
target
的絕對值大於sum
時,不可能實現 -
從公式
2*left = target + sum
可以看出,2*left
一定時偶數,所以target + sum
也必須保證時偶數。
使用一維陣列的解題步驟:
-
確定dp陣列以及其下標的含義
dp[j]
表示裝滿揹包容量為j的有多少種方法 -
確定遞推公式
不考慮
nums[i]
的情況下,填滿容量為j - nums[i]
的揹包,有dp[j - nums[i]]
種方法。也就是當前填滿容量為
j
的揹包的方法數 = 之前填滿容量為j
j - nums[i]
的方法數。於是得到遞推公式為:dp[j] = dp[j] + dp[j - nums[i]]
-
dp陣列的初始化
用0件物品(在下面的程式碼中可以看到遍歷物品的i是從下標0開始的,所以此時並沒有物品)裝滿揹包容量為0的揹包,有一種方法。
所以
dp[0] = 1
-
確定遍歷順序
一維dp解決0-1揹包問題,先正序遍歷物品,再倒敘遍歷揹包。
-
舉例推導dp陣列(略)
C++
class Solution { public: int findTargetSumWays(vector<int>& nums, int target) { int sum = 0; for (int i = 0; i < nums.size(); i++) { sum += nums[i]; } if (abs(target) > sum || ( target + sum ) % 2 == 1) { return 0; } int bagSize = ( sum + target ) / 2; vector<int> dp(bagSize + 1, 0); dp[0] = 1; for (int i = 0; i < nums.size(); i++) { for (int j = bagSize; j >= nums[i]; j--) { dp[j] += dp[j - nums[i]]; } } return dp[bagSize]; } };
JavaScript
/** * @param {number[]} nums * @param {number} target * @return {number} */ var findTargetSumWays = function(nums, target) { let sum = 0; for (let i = 0; i < nums.length; i++) { sum += nums[i]; } if (Math.abs(target) > sum || (sum + target) % 2 == 1) { return 0; } let bagSize = (sum + target) / 2; const dp = new Array(bagSize + 1).fill(0); dp[0] = 1; for (let i = 0; i < nums.length; i++) { for (let j = bagSize + 1; j >= nums[i]; j--) { dp[j] += dp[j - nums[i]]; } } return dp[bagSize]; };
-
時間複雜度:O(n × m),n為正數個數,m為揹包容量
-
空間複雜度:O(m)
另外,還可以用二維陣列解決。不過需要注意dp陣列的初始化,因為nums[0] = 0
是一種特殊的情況。給出C++程式碼如下。
class Solution { public: int findTargetSumWays(vector<int>& nums, int target) { int sum = 0; for (int i = 0; i < nums.size(); i++) { sum += nums[i]; } if (abs(target) > sum || ( target + sum ) % 2 == 1) { return 0; } int bagSize = ( sum + target ) / 2; vector<vector<int>> dp(nums.size(),vector<int>(bagSize + 1, 0)); // 特殊!!!!!!!!!!!!!!!!!!!!!! // 因為 0 既可以用 -0 表示, 也可以用 +0 表示 // 所以將 0 (不是物品的下標,而是物品的重量或價值) 填滿容量揹包為 0 的揹包中,有兩種方法 if (nums[0] == 0) { dp[0][0] = 2; } else { dp[0][0] = 1; } for (int j = 1; j < bagSize + 1; j++) { if (j == nums[0]) dp[0][j] = 1; } for (int i = 1; i < nums.size(); i++) { for (int j = 0; j < bagSize + 1; j++) { if (j < nums[i]) { dp[i][j] = dp[i - 1][j]; } else { dp[i][j] = dp[i - 1][j] + dp[i - 1][j - nums[i]]; } } } return dp[nums.size() - 1][bagSize]; } };