本文已参与「新人创作礼」活动,一起开启掘金创作之路。
698. 划分为k个相等的子集
给定一个整数数组 nums 和一个正整数 k,找出是否有可能把这个数组分成 k 个非空子集,其总和都相等。
示例 1:
输入: nums = [4, 3, 2, 3, 5, 2, 1], k = 4
输出: True
说明: 有可能将其分成 4 个子集(5),(1,4),(2,3),(2,3)等于总和。
示例 2:
输入: nums = [1,2,3,4], k = 3
输出: false
提示:
1 <= k <= len(nums) <= 16
0 < nums[i] < 10000
每个元素的频率在 [1,4] 范围内
思路
- 假设有解,可分为k组,每一组是多少?
若是要达到有解的目的,那么 肯定满足这样的式子:
那么,我们的第一个条件肯定是要满足不符合整除的直接丢弃
- 如何检查是否可以把这些元素划分到k组内呢?
若是要达到这一目的,我们最粗暴的方式是进行枚举,即:
看上去这个时间复杂度非常高,但是我们可以剪一剪枝
- 剪枝方法
- 可行性剪枝,我们把dfs分为k个阶段,上一阶段不满足就直接返回,不继续下去剪枝
- 顺序性优化,在可行性剪枝的条件下,我们可以发现,先选较大的,可以减少拼接次数发现合理与否 所以,我们可以对于原数组从大到小去选取
end
代码
class Solution {
public:
bool canPartitionKSubsets(vector<int>& nums, int k) {
int res = accumulate(begin(nums), end(nums), 0);
if (res % k) return false;
int flag = 0; res /= k;
sort(nums.rbegin(), nums.rend());
vector<int> vis(nums.size());
function<void(int, int, int)> dfs =
[&](int cur, int cnt, int pre) -> void {
if (flag) return;
if (cnt == k) {
flag = 1;
return;
}
if (cur == res) return dfs(0, cnt + 1, 0);
for (int i = pre; i < nums.size(); i ++) {
if (vis[i] || nums[i] + cur > res) continue;
vis[i] = 1;
dfs(nums[i] + cur, cnt, i + 1);
if (flag) return;
vis[i] = 0;
if (cur == 0) return;
}
};
dfs(0, 0, 0);
return flag;
}
};