class Solution:
def canPartitionKSubsets(self, nums, k):
total = sum(nums)
if total % k != 0:
return False
target = total // k
nums.sort(reverse=True)
if nums[0] > target:
return False
used = [False] * len(nums)
def backtrack(start, k, curr_sum):
if k == 1:
return True
if curr_sum == target:
return backtrack(0, k - 1, 0)
for i in range(start, len(nums)):
if not used[i] and curr_sum + nums[i] <= target:
used[i] = True
if backtrack(i + 1, k, curr_sum + nums[i]):
return True
used[i] = False
return False
return backtrack(0, k, 0)
class Solution {
public:
bool canPartitionKSubsets(vector<int>& nums, int k) {
int total = accumulate(nums.begin(), nums.end(), 0);
if (total % k != 0) return false;
int target = total / k;
sort(nums.rbegin(), nums.rend());
if (nums[0] > target) return false;
vector<bool> used(nums.size(), false);
function<bool(int, int, int)> backtrack = [&](int start, int k, int curr_sum) {
if (k == 1) return true;
if (curr_sum == target) return backtrack(0, k - 1, 0);
for (int i = start; i < nums.size(); ++i) {
if (!used[i] && curr_sum + nums[i] <= target) {
used[i] = true;
if (backtrack(i + 1, k, curr_sum + nums[i]))
return true;
used[i] = false;
}
}
return false;
};
return backtrack(0, k, 0);
}
};
class Solution {
public boolean canPartitionKSubsets(int[] nums, int k) {
int total = 0;
for (int n : nums) total += n;
if (total % k != 0) return false;
int target = total / k;
Arrays.sort(nums);
int n = nums.length;
if (nums[n - 1] > target) return false;
boolean[] used = new boolean[n];
return backtrack(nums, used, 0, k, 0, target);
}
private boolean backtrack(int[] nums, boolean[] used, int start, int k, int currSum, int target) {
if (k == 1) return true;
if (currSum == target) return backtrack(nums, used, 0, k - 1, 0, target);
for (int i = start; i < nums.length; i++) {
if (!used[i] && currSum + nums[i] <= target) {
used[i] = true;
if (backtrack(nums, used, i + 1, k, currSum + nums[i], target))
return true;
used[i] = false;
}
}
return false;
}
}
var canPartitionKSubsets = function(nums, k) {
const total = nums.reduce((a, b) => a + b, 0);
if (total % k !== 0) return false;
const target = total / k;
nums.sort((a, b) => b - a);
if (nums[0] > target) return false;
const used = Array(nums.length).fill(false);
function backtrack(start, k, currSum) {
if (k === 1) return true;
if (currSum === target) return backtrack(0, k - 1, 0);
for (let i = start; i < nums.length; i++) {
if (!used[i] && currSum + nums[i] <= target) {
used[i] = true;
if (backtrack(i + 1, k, currSum + nums[i])) return true;
used[i] = false;
}
}
return false;
}
return backtrack(0, k, 0);
};
Given an array of positive integers nums
and an integer k
, the task is to determine if it is possible to partition nums
into k
non-empty subsets whose sums are all equal.
nums
must be used exactly once in one of the subsets.1 <= k <= len(nums)
and 1 <= nums[i] <= 10^4
.
Return true
if such a partition is possible, or false
otherwise.
At first glance, the problem looks similar to the classic subset sum or partition problem, but with the added twist of dividing into k
equal groups. The brute-force approach would be to try all possible ways to assign elements to k
groups and check if each group sums to the same value, but this quickly becomes infeasible as the array grows.
To optimize, we consider:
k
; otherwise, it's impossible.The main challenge is efficiently exploring the combinations without redundant work, which leads us to recursive backtracking with pruning.
The solution uses a recursive backtracking approach with several optimizations:
nums
. If it's not divisible by k
, return false
immediately.target = total_sum / k
.nums
in descending order to put large numbers first, which helps prune impossible branches quickly.used
array to keep track of which elements are already included in a subset.target
, start filling the next subset.true
; otherwise, return false
.This method efficiently explores only valid assignments, avoiding redundant or impossible paths.
Example: nums = [4, 3, 2, 3, 5, 2, 1]
, k = 4
20 / 4 = 5
, so each subset must sum to 5.[5, 4, 3, 3, 2, 2, 1]
true
.The backtracking explores possible combinations, but the sorted order and pruning prevent unnecessary work.
n
elements to k
groups, leading to O(k^n) time complexity.used
array and recursion stack.The "Partition to K Equal Sum Subsets" problem is a challenging variation of the subset sum problem. By checking divisibility, sorting, and using recursive backtracking with pruning, we can efficiently determine if a valid partition exists. The key insight is to build each subset incrementally, avoid redundant work, and exit early when impossible paths are detected. This makes the solution both elegant and practical for moderate input sizes.