1. 程式人生 > 實用技巧 >416. Partition Equal Subset Sum

416. Partition Equal Subset Sum

問題:

給定一組數,請問是否將其分為兩個陣列,使得二者和相等。

Note:
Each of the array element will not exceed 100.
The array size will not exceed 200.
 

Example 1:
Input: [1, 5, 11, 5]
Output: true
Explanation: The array can be partitioned as [1, 5, 5] and [11].

Example 2:
Input: [1, 2, 3, 5]
Output: false
Explanation: The array cannot be partitioned into equal sum subsets.

  

解法:DP(動態規劃) 0-1 knapsack problem(0-1揹包問題)

1.確定【狀態】:

  • 可選擇的數:前 i 個數
  • 和:s:0~全元素和/2

2.確定【選擇】:

  • 選擇當前的數nums[i]
  • 不選擇當前的數nums[i]

3. dp[i][s]的含義:

前 i 個數中,組成和=s 的可能是否存在。

4. 狀態轉移:

dp[i][s]= OR {

  • 選擇 nums[i]:dp[i-1][s-nums[i]]:=前 i-1 個元素可組成和為 s-nums[i] 的可能性
  • 不選擇 nums[i]:dp[i-1][s]:=前 i-1 個元素可組成和為 s 的可能性

}

5. base case:

  • dp[0][s]=false
  • dp[i][0]=true
  • dp[0][0]=true

程式碼參考:

 1 class Solution {
 2 public:
 3     //dp[i][s]: in first i items, sum is s, exists?
 4     //case_1,choose i-th item: dp[i-1][s-val[i]]
 5     //case_2,don't choose: dp[i-1][s]
 6     //dp[i][s] = case_1 OR case_2
 7     //base case: dp[0][s] = false
 8     //dp[i][0] = true
9 //dp[0][0] = true 10 bool canPartition(vector<int>& nums) { 11 bool res; 12 int sum = 0; 13 for(int n:nums){ 14 sum+=n; 15 } 16 if(sum%2) return false; 17 sum/=2; 18 vector<vector<bool>> dp(nums.size()+1, vector<bool>(sum+1, false)); 19 dp[0][0] = true; 20 for(int i = 1; i<=nums.size(); i++) { 21 for(int s = 1; s<=sum; s++) { 22 if(s-nums[i-1]<0) dp[i][s] = dp[i-1][s]; 23 else dp[i][s] = dp[i-1][s-nums[i-1]] || dp[i-1][s]; 24 } 25 } 26 return dp[nums.size()][sum]; 27 } 28 };

♻️ 優化:空間複雜度:2維->1維

去掉 i

將 s 倒序遍歷。

if(s-nums[i-1]<0) dp[i][s] = dp[i-1][s];
else dp[i][s] = dp[i-1][s-nums[i-1]] || dp[i-1][s];

上述狀態轉移中,i 代表行,s 代表列

則都為 更新本行 : 使用上一行的 本列前面的列 來計算覆蓋本列

順序遍歷的話,更新後面列的時候,會用到已經被覆蓋掉的本列。而我們期望用的是上一行的本列

因此 將 s 倒序遍歷。更新前面的列,用的上一行本列,還未被本行操作更新。

程式碼參考:

 1 class Solution {
 2 public:
 3     //dp[i][s]: in first i items, sum is s, exists?
 4     //case_1,choose i-th item: dp[i-1][s-val[i]]
 5     //case_2,don't choose: dp[i-1][s]
 6     //dp[i][s] = case_1 OR case_2
 7     //base case: dp[0][s] = false
 8     //dp[i][0] = true
 9     //dp[0][0] = true
10     bool canPartition(vector<int>& nums) {
11         bool res;
12         int sum = 0;
13         for(int n:nums){
14             sum+=n;
15         }
16         if(sum%2) return false;
17         sum/=2;
18         vector<bool> dp(sum+1, false);
19         dp[0] = true;
20         for(int i = 1; i<=nums.size(); i++) {
21             for(int s = sum; s>0; s--) {
22                 if(s-nums[i-1]>=0) {
23                     dp[s] = dp[s-nums[i-1]] || dp[s];
24                 }
25             }
26         }
27         return dp[sum];
28     }
29 };