Problem: 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
文章目录
- 思路
- 解题方法
- Code
思路
这道题的目标是将给定的整数数组分成 k 个非空子集,使得每个子集的总和都相等。这可以通过深度优先搜索(DFS)来解决,同时使用备忘录(memo)来优化搜索过程。
解题方法
- 总和计算:首先计算整数数组的总和,如果总和不能被 k 整除,那么无法分成 k 个总和相等的子集,直接返回 false。
- 目标子集和:计算每个子集应该达到的目标和,即总和除以 k。
- DFS 搜索:使用深度优先搜索来探索所有可能的子集组合。在搜索的过程中,记录哪些元素已经被选择,以及当前子集的总和。
- 备忘录
(memo)
:为了避免重复计算,使用备忘录(memo)来存储已经计算过的状态。备忘录的键是一个整数 vis,它用位掩码的方式表示哪些元素已经被选择,以及当前的子集总和。这样可以避免在相同状态下的重复搜索。
最终返回 dfs
函数的结果,判断是否可以成功将数组分成 k 个总和相等的子集。
Code
class Solution {
public:
// int currentSum = 0;
// 使用 备忘录
unordered_map<int,bool> memo ;
bool dfs(vector<int>& nums,int & vis,int target,int start,int k,int currentSum){
if(k == 0) return true ; // 分组完成
if(currentSum == target) {
// 继续分下一个组
// currentSum = 0 ;
bool res = dfs(nums,vis,target,0,k-1,0) ;
memo[vis] = res ;
return res ;
}
if(memo.count(vis)) {
return memo[vis] ;
}
for(int i = start ; i<nums.size() ;i++) {
if( (vis >> i) & 1 == 1 ) {
// 判断第 i位是否是1 ,即有没有选择第i个Num
continue ;
}
if( currentSum +nums[i] <= target) {
vis |= 1<<i ;// 标记
if(dfs(nums,vis,target,i+1,k,currentSum+nums[i])) return true ;
vis ^= 1<<i ;// 标记
}
}
return false ;
}
bool canPartitionKSubsets(vector<int>& nums, int k) {
int n = nums.size() ;
int sum = 0 ;
sort(nums.begin() ,nums.end()) ;
for(int i = 0; i <n ; i++ ) {
sum+=nums[i] ;
}
if(sum %k !=0) return false ;
int target = sum/k ;
// vector<bool> vis(nums.size(),false ) ;
int vis = 0 ; // 用来记录每个元素是否被 选择过
// 一开始是0 对应二进制是 000.0000表示还没选择
return dfs(nums,vis,target,0,k,0);
}
};