题目描述:
Given an array of integers nums
and a positive integer k
, find whether it's possible to divide this array into k
non-empty subsets whose sums are all equal.
Example 1:
Input: nums = [4, 3, 2, 3, 5, 2, 1], k = 4
Output: True
Explanation: It's possible to divide it into 4 subsets (5), (1, 4), (2,3), (2,3) with equal sums.
Note:
1 <= k <= len(nums) <= 16
.0 < nums[i] < 10000
.
思路:(递归)
在416https://blog.csdn.net/orangefly0214/article/details/96277978的基础上加大了难度,可以想到之前的2Sum,到ksum的问题,最后可以递归的转换为最小的单元进行求解。
一样的思路,首先我们需要求出数组的总和,如果它可以整除k,说明有可能可以分成K个相等的子数组;否则,一定不能分成K个相等的子数组。
这样我们的目标转换为target=sum/k,和416不同的是,我们不是一分为二(一分为二的话,我们判断其中是否存在一份满足目标即可),而是需要分成多份,这时我们只能通过递归的方式来判断是否能分成K份,且如果有m个元素之和满足条件target,则这m个元素是后面不能再使用的,所以我们需要一个标记数组来标记当前元素是否使用过。
在我们的递归函数中,递归截止的条件是K是否为1,若K为1(说明之前已经有k-1个子数组满足和为target,因为target是sum整除k的结果,所以剩下的元素必能组成最后一个和为K的数组)。
若整个数组遍历完仍然没有返回,则返回false.
实现:
public boolean canPartitionKSubsets(int[] nums, int k) {
int s=0;
for(int num:nums){
s+=num;
}
if(k<=0||s%k!=0) return false;
int target=s/k;
boolean[] flag=new boolean[nums.length];//用来记录当前元素是否被使用过
return fun(nums,target,0,0,k,flag);
}
private boolean fun(int[] nums,int target,int start,int sum,int k,boolean[] flag){
if(k==1) return true;
if(sum==target){
return fun(nums,target,0,0,k-1,flag);
}
for(int i=start;i<nums.length;i++){
if(!flag[i]){
flag[i]=true;//访问过某个元素
if(fun(nums,target,i+1,sum+nums[i],k,flag)){
return true;
}
//若加上访问的这个元素后,后面无法构成满足和为target的数,则进行回退,
//表示我们暂时不访问这个元素
flag[i]=false;
}
}
return false;
}