一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第20天,点击查看活动详情。
题目描述
给定一个整数数组
nums和一个正整数k,找出是否有可能把这个数组分成k个非空子集,其总和都相等。
示例 1:
输入: nums = [4, 3, 2, 3, 5, 2, 1], k = 4
输出: True
说明: 有可能将其分成 4 个子集(5),(1,4),(2,3),(2,3)等于总和。
题目分析
先理一下这个题目,我们是希望在k个桶中放入总共n个数,使得各桶中的数字之和相等。所以这个题目涉及各种选择尝试,且不是可重用子问题相关,我们选择使用回溯法解答该题,使用track数组存储各桶中的数字之和,终止回溯的条件是传入的track状态数组各元素都等于目标值target,该target就是初始nums数组各元素之和除以k,因为nums各元素都要求是大于零的整数,所以target肯定也要是正整数。在选择&撤销选择环节我们就往track数组对应元素加上或者减掉nums[idx]的值。
回溯解法
class Solution {
int[] visited;
int sum;
int target;
public boolean canPartitionKSubsets(int[] nums, int k) {
sum = IntStream.of(nums).sum();
if(sum % k != 0) return false;
int n = nums.length;
visited = new int[n];
Arrays.sort(nums);
target = sum / k;
return dfs(nums, k, 0, 0);
}
public boolean dfs(int[] nums, int k, int start, int cnt){
if(k == 1){
return true;
}
if(cnt == target){
return dfs(nums, k - 1, 0, 0);
}
for(int i = start; i < nums.length; i++){
if(visited[i] == 1) continue;
if(cnt + nums[i] > target) break;
visited[i] = 1;
if(dfs(nums, k, i, cnt + nums[i])) return true;
visited[i] = 0;
}
return false;
}
}
暴力模拟
分析:暴力解法也可以行,主要思想跟大部分暴力方法相似,将第pos个数依次放到k个子集中的一个。剪枝2的思想就是:如果两个子集的和相同,那么当前第pos个数放到两个子集所得的结果也是一样的,所以可以直接跳过第二个子集。
注意:暴力虽香,但是耗时太长。看自己选择吧
class Solution {
public boolean canPartitionKSubsets(int[] nums, int k) {
if (k > nums.length) return false;
int sum = 0;
for (int v : nums) sum += v;
if (sum % k != 0) return false;
int used = 0;
int target = sum / k;
return backtrack(k, 0, nums, 0, used, target);
}
HashMap<Integer, Boolean> memo = new HashMap<>();
boolean backtrack(int k, int bucket,
int[] nums, int start, int used, int target) {
if (k == 0) {
return true;
}
if (bucket == target) {
boolean res = backtrack(k - 1, 0, nums, 0, used, target);
memo.put(used, res);
return res;
}
if (memo.containsKey(used)) {
return memo.get(used);
}
for (int i = start; i < nums.length; i++) {
if (((used >> i) & 1) == 1) {
continue;
}
if (nums[i] + bucket > target) {
continue;
}
used |= 1 << i;
bucket += nums[i];
if (backtrack(k, bucket, nums, i + 1, used, target)) {
return true;
}
used ^= 1 << i;
bucket -= nums[i];
}
return false;
}
}