題目
給定一個整數數組 nums 和一個正整數 k,找出是否有可能把這個數組分成 k 個非空子集,其總和都相等。
示例 1:
輸入: nums = [4, 3, 2, 3, 5, 2, 1], k = 4
輸出: True
說明: 有可能將其分成 4 個子集(5),(1,4),(2,3),(2,3)等於總和。
注意:
1 <= k <= len(nums) <= 16
0 < nums[i] < 10000
解題思路
- 首先可以計算出每個子集的和的值 average = sum / k;此后就圍繞着這個值進行
- 之后就是需要組成將nums中的值,組成 7 個 average
- 其次明確遞歸函數的目的與作用 : 將函數的最大值,放入到一個合適的位置
- 具體思路隨代碼
代碼
class Solution { public: bool canPartitionKSubsets(vector<int>& nums, int k) { int sum = accumulate(nums.begin(), nums.end(), 0); if(sum % k != 0) return false; int average = sum / k; sort(nums.begin(), nums.end()); if(nums.back() > average) return false; while(nums.size() && nums.back() == average) { nums.pop_back(); k -= 1; } //在這之上都只是一些基礎的判斷准備工作
vector<int> flag(k, 0); int ret = Partition(flag, nums, average); return ret; } private: bool Partition(vector<int> &flag, vector<int> nums, int average)
//這里nums必須傳值,而flag傳引用即可,稍后說明
//flag長度為k,目的就是flag中的k個值,每個達到average
//每次將最大的值放入到flag中的一個位置(相加),保證不超過average { if(!nums.size()) return true;
//如果按照要求一個一個放入,最后nums空了,即說明成功,返回true int max = nums.back(); nums.pop_back(); //彈出最大值 for(auto &c : flag) { if(c + max <= average) { c += max;
//1,在flag中尋找合適的位置,相加
//2,相加值小於average即表示有可能
//3,只是嘗試性的加入,但是在函數開頭我們是將值彈出的,所以需要值傳遞nums
if(Partition(flag, nums, average)) return true;
//nums從大到小逐個遞歸,如果所有的nums都成功放入flags,返回true
//如果有失敗,則說明這個嘗試失敗 c -= max;
//4,如果失敗flag會修改回來,所以使用引用即可 } if(c == 0) break;
//這個地方原本值為0結果失敗,flag之后的值也為0,沒有必要嘗試 } return false;
//只有遞歸返回true才說明成功 }; };