[LeetCode] Partition to K Equal Sum Subsets 分割K个等和的子集

简介:

Given an array of integers nums and a positive integer k, find whether it's possible to divide this array into knon-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.

这道题给了我们一个数组nums和一个数字k,问我们该数字能不能分成k个非空子集合,使得每个子集合的和相同。给了k的范围是[1,16],而且数组中的数字都是正数。这跟之前那道Partition Equal Subset Sum很类似,但是那道题只让分成两个子集合,所以问题可以转换为是否存在和为整个数组和的一半的子集合,可以用dp来做。但是这道题让求k个和相同的,感觉无法用dp来做,因为就算找出了一个,其余的也需要验证。这道题我们可以用递归来做,首先我们还是求出数组的所有数字之和sum,首先判断sum是否能整除k,不能整除的话直接返回false。然后需要一个visited数组来记录哪些数组已经被选中了,然后调用递归函数,我们的目标是组k个子集合,是的每个子集合之和为target = sum/k。我们还需要变量start,表示从数组的某个位置开始查找,curSum为当前子集合之和,在递归函数中,如果k=1,说明此时只需要组一个子集合,那么当前的就是了,直接返回true。如果curSum等于target了,那么我们再次调用递归,此时传入k-1,start和curSum都重置为0,因为我们当前又找到了一个和为target的子集合,要开始继续找下一个。否则的话就从start开始遍历数组,如果当前数字已经访问过了则直接跳过,否则标记为已访问。然后调用递归函数,k保持不变,因为还在累加当前的子集合,start传入i+1,curSum传入curSum+nums[i],因为要累加当前的数字,如果递归函数返回true了,则直接返回true。否则就将当前数字重置为未访问的状态继续遍历,参见代码如下:

解法一:

public:
    bool canPartitionKSubsets(vector<int>& nums, int k) {
        int sum = accumulate(nums.begin(), nums.end(), 0);
        if (sum % k != 0) return false;
        vector<bool> visited(nums.size(), false);
        return helper(nums, k, sum / k, 0, 0, visited);
    }
    bool helper(vector<int>& nums, int k, int target, int start, int curSum, vector<bool>& visited) {
        if (k == 1) return true;
        if (curSum == target) return helper(nums, k - 1, target, 0, 0, visited);
        for (int i = start; i < nums.size(); ++i) {
            if (visited[i]) continue;
            visited[i] = true;
            if (helper(nums, k, target, i + 1, curSum + nums[i], visited)) return true;
            visited[i] = false;
        }
        return false;
    }
}; 

下面这种方法也挺巧妙的,思路是建立长度为k的数组v,只有当v里面所有的数字都是target的时候,才能返回true。我们还需要给数组排个序,由于题目中限制了全是正数,所以数字累加只会增大不会减小,一旦累加超过了target,这个子集合是无法再变小的,所以就不能加入这个数。实际上相当于贪婪算法,由于题目中数组数字为正的限制,有解的话就可以用贪婪算法得到。我们用一个变量idx表示当前遍历的数字,排序后,我们从末尾大的数字开始累加,我们遍历数组v,当前位置加上nums[idx],如果超过了target,我们掉过继续到下一个位置,否则就调用递归,此时的idx为idx-1,表示之前那个数字已经成功加入数组v了,我们尝试着加下一个数字。如果递归返回false了,我们就将nums[idx]从数组v中对应的位置减去,还原状态,然后继续下一个位置。如果某个递归中idx等于-1了,表明所有的数字已经遍历完了,此时我们检查数组v中k个数字是否都为target,是的话返回true,否则返回false,参见代码如下:

解法二:

public:
    bool canPartitionKSubsets(vector<int>& nums, int k) {
        int sum = accumulate(nums.begin(), nums.end(), 0);
        if (sum % k != 0) return false;
        vector<int> v(k, 0);
        sort(nums.begin(), nums.end());
        return helper(nums, sum / k, v, (int)nums.size() - 1);
    }
    bool helper(vector<int>& nums, int target, vector<int>& v, int idx) {
        if (idx == -1) {
            for (int t : v) {
                if (t != target) return false;
            }
            return true;
        }
        int num = nums[idx];
        for (int i = 0; i < v.size(); ++i) {
            if (v[i] + num > target) continue;
            v[i] += num;
            if (helper(nums, target, v, idx - 1)) return true;
            v[i] -= num;
        }
        return false;
    }
};

参考资料:

https://discuss.leetcode.com/topic/107178/easy-to-understand-java-solution


本文转自博客园Grandyang的博客,原文链接:[LeetCode] Partition to K Equal Sum Subsets 分割K个等和的子集

,如需转载请自行联系原博主。

相关文章
|
3月前
|
算法
LeetCode第90题子集II
LeetCode第90题"子集II"的解题方法,通过排序和回溯算法生成所有不重复的子集,并使用一个boolean数组来避免同一层中的重复元素,展示了解决这类问题的编码技巧。
LeetCode第90题子集II
|
3月前
|
Python
【Leetcode刷题Python】416. 分割等和子集
LeetCode 416题 "分割等和子集" 的Python解决方案,使用动态规划算法判断是否可以将数组分割成两个元素和相等的子集。
31 1
|
3月前
|
Python
【Leetcode刷题Python】131. 分割回文串
LeetCode题目131的Python编程解决方案,题目要求将给定字符串分割成所有可能的子串,且每个子串都是回文串,并返回所有可能的分割方案。
21 2
|
3月前
|
索引 Python
【Leetcode刷题Python】78. 子集
LeetCode题目78的Python编程解决方案,题目要求给定一个互不相同的整数数组,返回该数组所有可能的子集(幂集),且解集中不能包含重复的子集。
23 1
|
3月前
|
算法
LeetCode第78题子集
文章分享了LeetCode第78题"子集"的解法,使用递归和回溯算法遍历所有可能的子集,展示了将子集问题视为树形结构进行遍历的解题技巧。
|
5月前
|
存储 算法 数据可视化
LeetCode 132题详解:使用动态规划与中心扩展法解决分割回文串 II 的最少分割次数问题
LeetCode 132题详解:使用动态规划与中心扩展法解决分割回文串 II 的最少分割次数问题
|
5月前
|
存储 算法 数据可视化
LeetCode 131题详解:高效分割回文串的递归与动态规划方法
LeetCode 131题详解:高效分割回文串的递归与动态规划方法
|
2月前
|
Unix Shell Linux
LeetCode刷题 Shell编程四则 | 194. 转置文件 192. 统计词频 193. 有效电话号码 195. 第十行
本文提供了几个Linux shell脚本编程问题的解决方案,包括转置文件内容、统计词频、验证有效电话号码和提取文件的第十行,每个问题都给出了至少一种实现方法。
LeetCode刷题 Shell编程四则 | 194. 转置文件 192. 统计词频 193. 有效电话号码 195. 第十行
|
3月前
|
Python
【Leetcode刷题Python】剑指 Offer 32 - III. 从上到下打印二叉树 III
本文介绍了两种Python实现方法,用于按照之字形顺序打印二叉树的层次遍历结果,实现了在奇数层正序、偶数层反序打印节点的功能。
57 6
|
3月前
|
搜索推荐 索引 Python
【Leetcode刷题Python】牛客. 数组中未出现的最小正整数
本文介绍了牛客网题目"数组中未出现的最小正整数"的解法,提供了一种满足O(n)时间复杂度和O(1)空间复杂度要求的原地排序算法,并给出了Python实现代码。
114 2