给定一个整数数组 nums 和一个正整数 k,找出是否有可能把这个数组分成 k 个非空子集,其总和都相等。
示例 1:
输入: nums = [4, 3, 2, 3, 5, 2, 1], k = 4
输出: True
说明: 有可能将其分成 4 个子集(5),(1,4),(2,3),(2,3)等于总和。
思路:
1.计算出每个子集的和,为 sum = sum(nums) / k
2.将nums升序排序,如果最大值大于每个子集的和,返回false
3.定义大小为k的数组arr,相当于k个桶,每个桶的初始值为sum
4.从nums最后一个数开始递归,递归函数将每一个数加入到合适的桶里面去
什么时候这个数加入这个桶呢? 当arr[i] == nums[i] || (cur >0 && arr[i] - nums[cur] >= nums[0])
即这个桶正好能放这个数,或者这个桶放完这个数之后至少还能放最小的数
放完之后桶的容量要减少,然后递归放下一个数,如果这个数不能放在这个桶里面,要从桶里拿出这个数
#include<iostream>
#include<vector>
#include<string>
#include<algorithm>
using namespace std;
class Solution {
public:
bool canPartitionKSubsets(vector<int>& nums, int k) {
int sum = 0;
for(int i = 0; i < nums.size(); i++) {
sum+=nums[i];
}
if(sum % k != 0) {
return false;
}
sum/=k; //每个子集之和
sort(nums.begin(),nums.end());
if(nums[nums.size()-1] > sum) { //最大的元素大于子集之和,说明不可能构成k个sum子集
return false;
}
vector<int> arr(k,sum); //k个容量为sum的桶
return forEveryNum(nums,nums.size()-1,arr,k);
}
bool forEveryNum(vector<int> &nums,int cur,vector<int> &arr,int k) {
if(cur < 0) {
return true;
}
//对每个数,遍历k个桶,选择一个放入
for(int i = 0; i < k; i++) {
if(arr[i] == nums[cur] || (cur > 0 && arr[i] - nums[cur] > nums[0]) {
arr[i] -= nums[cur];
if(forEveryNum(nums,cur-1,arr,k)) {
return true;
}
//这个数不能放在这个桶里面,从桶中拿回这个数
arr[i] += nums[cur];
}
}
return false;
}
};
int main() {
int n,k,data;
vector<int> nums;
cin>>n;
for(int i = 0; i < n; i++) {
cin>>data;
nums.push_back(data);
}
cin>>k;
Solution s;
int res = s.canPartitionKSubsets(nums,k);
if(res == 1) {
cout<<"true";
}
else {
cout<<"false";
}
return 0;
}