题目
给定一个整数数组 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才说明成功 }; };