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(), );
if (sum % k != ) return false;
vector<bool> visited(nums.size(), false);
return helper(nums, k, sum / k, , , visited);
}
bool helper(vector<int>& nums, int k, int target, int start, int curSum, vector<bool>& visited) {
if (k == ) return true;
if (curSum == target) return helper(nums, k - , target, , , visited);
for (int i = start; i < nums.size(); ++i) {
if (visited[i]) continue;
visited[i] = true;
if (helper(nums, k, target, i + , curSum + nums[i], visited)) return true;
visited[i] = false;
}
return false;
}
};

我们也可以对上面的解法进行一些优化,比如先给数组按从大到小的顺序排个序,然后在递归函数中,我们可以直接判断,如果curSum大于target了,直接返回false,因为题目中限定了都是正数,并且我们也给数组排序了,后面的数字只能更大,这个剪枝操作大大的提高了运行速度,感谢热心网友 hellow_world00 提供,参见代码如下:

解法二:

class Solution {
public:
bool canPartitionKSubsets(vector<int>& nums, int k) {
int sum = accumulate(nums.begin(), nums.end(), );
if (sum % k != ) return false;
sort(nums.begin(), nums.end(), greater<int>());
vector<bool> visited(nums.size(), false);
return helper(nums, k, sum / k, , , visited);
}
bool helper(vector<int>& nums, int k, int target, int start, int curSum, vector<bool>& visited) {
if (k == ) return true;
if (curSum > target) return false;
if (curSum == target) return helper(nums, k - , target, , , visited);
for (int i = start; i < nums.size(); ++i) {
if (visited[i]) continue;
visited[i] = true;
if (helper(nums, k, target, i + , 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(), );
if (sum % k != ) return false;
vector<int> v(k, );
sort(nums.begin(), nums.end());
return helper(nums, sum / k, v, (int)nums.size() - );
}
bool helper(vector<int>& nums, int target, vector<int>& v, int idx) {
if (idx == -) {
for (int t : v) {
if (t != target) return false;
}
return true;
}
int num = nums[idx];
for (int i = ; i < v.size(); ++i) {
if (v[i] + num > target) continue;
v[i] += num;
if (helper(nums, target, v, idx - )) return true;
v[i] -= num;
}
return false;
}
};

类似题目:

Partition Equal Subset Sum

参考资料:

https://leetcode.com/problems/partition-to-k-equal-sum-subsets/

https://leetcode.com/problems/partition-to-k-equal-sum-subsets/discuss/108730/javacstraightforward-dfs-solution

https://leetcode.com/problems/partition-to-k-equal-sum-subsets/discuss/108751/easy-to-understand-java-solution

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

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

  1. 【LeetCode】698. Partition to K Equal Sum Subsets 解题报告(Python & C++)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 回溯法 日期 题目地址:https://leetco ...

  2. LeetCode Partition to K Equal Sum Subsets

    原题链接在这里:https://leetcode.com/problems/partition-to-k-equal-sum-subsets/description/ 题目: Given an arr ...

  3. [LeetCode] 698. Partition to K Equal Sum Subsets

    Problem Given an array of integers nums and a positive integer k, find whether it's possible to divi ...

  4. 【leetcode】698. Partition to K Equal Sum Subsets

    题目如下: 解题思路:本题是[leetcode]473. Matchsticks to Square的姊妹篇,唯一的区别是[leetcode]473. Matchsticks to Square指定了 ...

  5. [Swift]LeetCode698. 划分为k个相等的子集 | Partition to K Equal Sum Subsets

    Given an array of integers nums and a positive integer k, find whether it's possible to divide this ...

  6. 698. Partition to K Equal Sum Subsets

    Given an array of integers nums and a positive integer k, find whether it's possible to divide this ...

  7. 698. 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 ...

  8. Partition to K Equal Sum Subsets

    Given an array of integers nums and a positive integer k, find whether it's possible to divide this ...

  9. [LeetCode] Split Array with Equal Sum 分割数组成和相同的子数组

    Given an array with n integers, you need to find if there are triplets (i, j, k) which satisfies fol ...

随机推荐

  1. 云服务器Windows Server2012 配置http服务器(又称Web服务器,IIS)

    出错:无法打开运行空间池.服务器管理器WinRM插件可能已损坏或丢失. 解决方法: http://shiyousan.com/post/636308065767125916 第一步是开启WinRM服务 ...

  2. php设计模式八-----装饰器模式

    1.介绍: 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构.这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装. 这种模式创建了一个装饰 ...

  3. 使用git将文件上传到Coding

    1,首先在Coding上新建项目. 2,填写项目的相关内容. 3,建立项目后复制下面鼠标所选内容. 4,在自己的电脑中建立文件夹. 5,进入该文件夹后,点击鼠标右键,然后再点Git  Clone. 6 ...

  4. 【福大软工】 W班级总成绩排名3

    评分链接: alpha测试    软件产品案例分析 总分排名: 团队千帆竞发图 总结: 本次排名是alpha测试    软件产品案例分析 两次排名的汇总. 1.alpha测试小组评价: 听说:10篇冲 ...

  5. 第二次作业评分可能要 delay 一些

    各位同学,因为我现在在出差,昨天刚刚到旧金山,加上倒时差,所以这次作业我处理得会更慢一些,希望谅解. 另外,博客园的邮件通知邮件好多都进垃圾箱了,所以如果你有什么问题我没回且你关心的,请给我写邮件:j ...

  6. 网络1712--c语言函数作业总结

    作业亮点 1.总体情况 很多同学在思路方面大部分写的都很详细,能够通过思路回顾自己的代码 大部分同学都认真完成PTA,也充分利用了函数来解题 大部分同学能够从上机考试中总结自己的失误和不足点,制订了自 ...

  7. 20162330 第十二周 蓝墨云班课 hash

    题目要求 利用除留余数法为下列关键字集合的存储设计hash函数,并画出分别用开放寻址法和拉链法解决冲突得到的空间存储状态(散列因子取0.75) 关键字集合:85,75,57,60,65,(你的8位学号 ...

  8. 201621123060《JAVA程序设计》第十周学习总结

    1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结异常相关内容. 2. 书面作业 本次PTA作业题集异常 1. 常用异常 结合题集题目7-1回答 1.1 自己以前编写的代码中经常出现 ...

  9. sqlserver学习_01

    sqlserver的学习成长之路,每一个技术的学习过程都是值得让人回味的,现在百度上关于sqlser的资料很多,但是都太杂,希望能为大家分享一点简单易懂的干货,跟大家一起进步学习. 一.建表 1.创建 ...

  10. Python之旅.第三章.函数3.29

    一.无参装饰器 1 开放封闭原则 软件一旦上线后,就应该遵循开放封闭原则,即对修改源代码是封闭的,对功能的扩展是开放的 也就是说我们必须找到一种解决方案: 能够在不修改一个功能源代码以及调用方式的前提 ...