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.
给出一个数组,要分成k个非空子数组,使每个子数组的和都相等。
思路:
每个小组可以是不连续的,考虑DFS遍历
首先求数组的和sum,因为每个小组的和相等,所以每个小组的和应该是sum/k。当sum/k不是整数时,直接返回false。
当一个小组满足和为sum/k时,到下一小组,k减去1,同时和清零。
DFS的流程:
dfs(...):
边界条件
for i = start to end
if(visited[i]) continue
visited[i] = true
dfs(...)
visited[i] = false
这里边界条件就是和到了sum/k时,就要继续寻找下一小组,小组数还剩下0时,就返回true
所以要传入现在计算的和,目标和sum/k,还有剩下的小组数k
为了方便剪枝,先把数组排序,从大到小遍历,这样当前和超过sum/k,就不需要再迭代下去了。
但是这个剪枝的判断不要写在循环里,因为在循环里一旦返回false,整个循环就结束了,后面小的也不会再遍历了。所以也放在边界条件里面。
class Solution {
boolean[] visited;
int n = 0;
public boolean canPartitionKSubsets(int[] nums, int k) {
n = nums.length;
int sum = 0;
for(int i = 0; i < n; i ++) {
sum += nums[i];
}
if(sum % k != 0) {
return false;
}
visited = new boolean[n];
Arrays.sort(nums);
return dfs(nums, k, n-1, sum/k, 0);
}
boolean dfs(int[] nums, int k, int start, int target, int cur) {
if(k == 0) {
return true;
}
if(cur > target) {
return false;
}
if(cur == target) {
return dfs(nums, k-1, n-1, target, 0);
}
for(int i = start; i >= 0; i --) {
if(visited[i]) continue;
visited[i] = true;
if(dfs(nums, k, i-1, target, nums[i]+cur)) return true;
visited[i] = false;
}
return false;
}
}