這題的暴力想法就是,每遇到一個 element,都可以決定要不要把這個 element 加進目前的 subset(是不是跟 0/1 背包問題很像呢?都是每遇到一個 item 都可以分成要不要把目前這個 item 加入背包),如果可以找到一個 subset sum 是原本 array sum 的一半,那剩下的就可以形成另一個 subset,而且 sum 也是原本 array sum 的一半。
實作如下:
classSolution {public:boolcanPartition(vector<int>& nums) {int sum=0;for(auto n: nums) sum += n; // If sum is odd, it's impossible to have two subsets with equal sumif(sum %2!=0)returnfalse;returnrecursivePartition(nums, sum/2,0); }private:boolrecursivePartition(vector<int>& nums,int sum,int curIdx) {if(sum ==0)returntrue;if(nums.size() ==0|| curIdx >=nums.size())returnfalse; // Use the value of curIdxif(nums[curIdx] <= sum) {if(recursivePartition(nums, sum-nums[curIdx], curIdx+1)) returntrue; } // Don't use the value of curIdxreturnrecursivePartition(nums, sum, curIdx+1); }};
不意外地,上面的方法會 TLE。
解法二 - Recursive + Memoization
要改善上面的方法,就是要用另一個 array 來儲存 function 的計算結果。因為我們同時要考慮 sum 跟 curIdx 兩個變數,所以需要用一個 2D 的 table 來儲存。雖然我們的 table 裡面只需要存 true or false,但為了區分目前這格有沒有被算過,我們還是用 int 來存,-1 表示沒算過,0/1 表示 false/true。
classSolution {public:boolcanPartition(vector<int>& nums) {int sum=0;for(auto n: nums) sum += n; // If sum is odd, it's impossible to have two subsets with equal sumif(sum %2!=0)returnfalse; vector<vector<int>>memo(sum/2+1,vector<int>(nums.size(),-1));returnrecursivePartition(nums, sum/2,0, memo); }private:boolrecursivePartition(vector<int>& nums,int sum,int curIdx,vector<vector<int>>& memo) {if(sum ==0) {returntrue; }if(nums.empty() || curIdx >=nums.size())returnfalse;if(memo[sum][curIdx] ==-1) {if(nums[curIdx] <= sum) { // Use the value of curIdxif(recursivePartition(nums, sum-nums[curIdx], curIdx+1, memo)) {memo[sum][curIdx] =1;returntrue; } } // Don't use the value of curIdxmemo[sum][curIdx] =recursivePartition(nums, sum, curIdx+1, memo) ?1:0; }returnmemo[sum][curIdx] ==1?true:false; }};
參考一下執行效率:
Runtime: 40 ms, faster than 72.06% of C++ online submissions for Partition Equal Subset Sum. Memory Usage: 69.3 MB, less than 5.88% of C++ online submissions for Partition Equal Subset Sum.