[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。否则就将当前数字重置为未访问的状态继续遍历,参见代码如下:

解法一:

class Solution {

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,参见代码如下:

解法二:

class Solution {

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

https://discuss.leetcode.com/topic/107185/java-c-straightforward-dfs-solution

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

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

时间: 2024-07-31 00:34:52

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

[LeetCode] Subarray Product Less Than K 子数组乘积小于K

Your are given an array of positive integers nums. Count and print the number of (contiguous) subarrays where the product of all the elements in the subarray is less than k. Example 1: Input: nums = [10, 5, 2, 6], k = 100 Output: 8 Explanation: The 8

k宝怎么用?农行k宝付款转账图文教程

K宝是中国农业银行提供的用于存放客户证书及私钥的物理介质,其外形类似于U盘,又称USB-KEY,使用时,直接将K宝插入电脑的USB接口上即可,但必须安装相应的支持软件. 注意:由于中国农业银行的疏忽,在Windows Vista 系统或Windows 7系统上可以将K宝中的证书导出!如您要导出,切记:不可以删除原证书!私钥被导出后,除了通过网页的方法其他方法不可写入! 第一步:打开农业银行网上银行主页http://www.95599.cn/或http://www.abchina.com/ 点击"

LeetCode 23 Merge k Sorted Lists(合并K个已排序链表)

翻译 合并K个已排序的链表,并且将其排序并返回. 分析和描述其复杂性. 原文 Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity. 代码 我们采用分治的方法来解决这个问题,其有K个链表,不断将其划分(partition),再将其归并(merge). 划分的部分并不难,将其不断分成两部分,但是需要注意的是可能出现start和end相等的情况,这时候就直接r

LeetCode 25 Reverse Nodes in k-Group(在K组链表中反转结点)

原文 给定一个链表,在一定时间内反转这个链表的结点,并返回修改后的链表. 如果结点数不是K的倍数,那么剩余的结点就保持原样. 你不应该在结点上修改它的值,只有结点自身可以修改. 只允许使用常量空间. 例如 给定链表: 1->2->3->4->5 对于 k = 2,你应该返回: 2->1->4->3->5 对于 k = 3,你应该返回: 3->2->1->4->5 翻译 Given a linked list, reverse the

c语言-C语言int *p,*q=NULL,k=1;p=&amp;amp;amp;k;以下选项中错误的赋值是

问题描述 C语言int *p,*q=NULL,k=1;p=&k;以下选项中错误的赋值是 若有下面定义和赋值:int *p,*q=NULL,k=1;p=&k;以下选项中错误的赋值是() A.*q=0;B.*p=k+1;C.p=0;D.q=''; ====参考答案是C,求教详细解释?? 解决方案 答案是A; 理由:p指向k,*q=NULL,q指向NULL *q没有分配空间所以不能赋值:A错误: *p指向K,即k=k+1; 正确 p=0;将保存地址清零:允许赋值:正确 q是指针,定义是已经分配指

百度K你,你也可以K它

中介交易 http://www.aliyun.com/zixun/aggregation/6858.html">SEO诊断 淘宝客 云主机 技术大厅 很多站长经常会报怨百度怎么不收录我站啦,怎么被百度K了,实际上你也可以K百度,哈哈. (就是拒绝从百度搜索到你网站,会显示来源非法!!) 下面教你一个方法吧. 很多网站有被百度K的经历,现在让我们来把百度给K了: 以下是引用片段: var strA="baidu"; var strB=document.referrer;

最近百度猛K站浅谈被K后快速重新被收录

中介交易 http://www.aliyun.com/zixun/aggregation/6858.html">SEO诊断 淘宝客 云主机 技术大厅 也许大家都有感觉到最近百度K站的力度是那个的猛,很多站点都被百度降权或是直接K掉.什么原因呢?百度在调整算法呗!之前的减肥产品,像左旋右 碱这些产品,搜索这个关键词出来的站基本都是站群,而且是很明显的,我们都能看到,百度自然也就清楚. 因为我的一个站相宜本草眼霜也给百度K了,是一页不剩的那种,这多少有点郁闷,不过网站被K是做站常见的事,只要好

LeetCode All in One 题目讲解汇总(持续更新中...)

终于将LeetCode的免费题刷完了,真是漫长的第一遍啊,估计很多题都忘的差不多了,这次开个题目汇总贴,并附上每道题目的解题连接,方便之后查阅吧~ 如果各位看官们,大神们发现了任何错误,或是代码无法通过OJ,或是有更好的解法,或是有任何疑问,意见和建议的话,请一定要在对应的帖子下面评论区留言告知博主啊,多谢多谢,祝大家刷得愉快,刷得精彩,刷出美好未来- 博主制作了一款iOS的应用"Leetcode Meet Me",里面有Leetcode上所有的题目,并且贴上了博主的解法,随时随地都能

算法:hdu 4597 Play Game(区间dp)

题意 Alice和Bob玩一个游戏,有两个长度为N的正整数数字序列,每次他们两个 只能从其中一个序 列,选择两端中的一个拿走.他们都希望可以拿到尽量大 的数字之和,并且他们都足够聪明,每次都选择 最优策略.Alice先选择,问 最终Alice拿到的数字总和是多少? 思路 这题应该算是区间dp吧, 可以看一下这题的原型: 其他规则都一样,但是只有一个数字序列,也是每次只能拿左右两端的一个数字 ,问最终Alice拿多少? (这个可以去做uva-10891) 只有一行数字序列可以用f(i, j)表示数