LeetCode698:划分为k个相等的子集(递归)
1、给定一个整数数组 nums 和一个正整数 k,找出是否有可能把这个数组分成 k 个非空子集,其总和都相等。
方法一:递归:
思路:
对于每个数来讲,其都有k个选择(就是k个桶),每个数都需要尝试放在第i(i>=0&&i<K)个桶里。只要当前尝试的第i个桶正好能放下当前的数或者放下当前的数后桶里的容量还有剩余,那么这个数就可以放在这个桶里,那么我们就可以进行对下一个数选择可以放入的桶了。
在进行递归函数之前,就进行了预处理,这个预处理是按升序排序,且能进入到递归函数时,就已经保证了数组对最后一个数是小于空桶的容量的。
/**
* 划分为k个相等的子集
* @param nums
* @param k
* @return
*/
public boolean canPartitionKSubsets(int[] nums, int k) {
if(k ==1){
return true;
}
int sum = 0;
for(int i=0;i<nums.length;i++){
sum += nums[i];
}
//如果不能整除,直接返回false
if(sum%k !=0){
return false;
}
//能整除,得到每个子集的和
int avg = sum/k;
//对数组nums进行排序
Arrays.sort(nums);
//如果数组的最大值大于每个子集的和,返回false
if(nums[nums.length-1] > avg){
return false;
}
//定义一个长度为k的桶
int[] buckets = new int[k];
//桶的每一个值都是子集的和,即空桶的容量为子集的和
Arrays.fill(buckets,avg);
//从数组最后一个数开始进行递归
return forEveryNum(nums,nums.length-1,buckets,k);
}
private boolean forEveryNum(int[] nums,int cur,int[] buckets,int k){
//递归的终止条件,cur走到-1时,说明所有的数全部都放进桶里了
if(cur <0){
return true;
}
//递归的内容
//对每个数来说,遍历k个桶,选择一个桶放进去
for(int i=0;i<k;i++){
//如果正好能放进去当前的数,或者放下当前的数后,桶里的容量还有剩余可以放进其他的数
if(nums[cur] == buckets[i] || (buckets[i]-nums[cur] >=nums[0])){
//把当前这个数nums[cur]放到这个桶i里,更新这个桶的容量
buckets[i] = buckets[i]-nums[cur];
//递归放下一个数nums[cur-1]
if(forEveryNum(nums,cur-1,buckets,k)){
//只需要找到一个即可
return true;
}else{
//这个数nums[cur]不应该放在桶i中,从桶中拿回当前的数nums[cur]
buckets[i] = buckets[i]+nums[cur];
}
}
}
return false;
}