文章目录
- 698. 划分为k个相等的子集
- 416. 分割等和数组
698. 划分为k个相等的子集
把一个数组,拆分成K个大小一样的子数组。方法可以是状态枚举,或者dfs
class Solution {
public:
bool canPartitionKSubsets(vector<int>& nums, int k) {
// 从大向小,提高效率
sort(nums.begin(), nums.end(),greater<int>());
int total = accumulate(nums.begin(), nums.end(), 0);
if (total%k != 0) {
return false;
}
int avg = total/k;
if (nums.front()>avg) return false;
int n = nums.size();
vector<int> sum(k,0);
// 使用dfs的方法,每次去枚举每一个队列里能不能有空间放开这个数字
function<bool(int)> dfs = [&](int x){
if(x == n) return true;
int cur = nums[x];
for (int i = 0;i<k;i++){
if(i!= 0 && sum[i] == sum[i-1]) continue; // 这个执行了一个剪枝,如果前一个状态和当前一样,就不需要考虑了。
sum[i] += cur;
if(sum[i]<=avg && dfs(x+1)){
return true;
}
sum[i] -= cur;
}
return false;
};
return dfs(0);
}
};
状态压缩的方法
class Solution {
public:
bool canPartitionKSubsets(vector<int>& nums, int k) {
sort(nums.begin(), nums.end());
int total = accumulate(nums.begin(), nums.end(), 0);
if (total%k != 0) {
return false;
}
int avg = total/k;
if (nums.back()>avg) return false;
int n = nums.size();
int size = 1<<n;
vector<int> dp(size, -1);
dp[0] = 0;
for(int i = 0;i<size;i++){
if(dp[i] == -1) continue;
for (int j = 0;j<n;j++){
if(dp[i] + nums[j] > avg) break;
if(((1<<j) & i) == 0){
int next = i+ (1<<j);
if(dp[next] != -1) continue;
dp[next] = (dp[i]+nums[j])% avg ;
}
if(dp[size-1] == 0) return true;
}
}
return false;
}
};
416. 分割等和数组
这个问题其实可以被看做是上一个问题的子问题,但是也可以被简化为其中一个数组能够正好凑到target。因此问题可以被化简为背包问题求解。
class Solution {
public:
bool canPartition(vector<int>& nums) {
int sum = 0;
int maxnum = 0;
for (auto x: nums){
sum += x;
maxnum = max(maxnum, x);
}
if(sum%2!=0) return false;
int target = sum/2;
if (maxnum>target) return false;
int n = nums.size();
if (n < 2) {
return false;
}
vector<bool> dp(target+1, false);
dp[0] = true;
for (int i = 0;i<n;i++){
int cur = nums[i];
for (int j = target;j>=cur;j--){
dp[j] = dp[j] || dp[j-cur];
}
}
return dp[target];
}
};