文章目录
- 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];}
};