开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第32天,点击查看活动详情 这也是第40篇文章
前言
这篇文章探讨的话题是——状态压缩。
说实话,写这篇博文的时候我还是有一定压力的,因为我对状态压缩也不是特别了解,现在只能说是边写边总结,如果说得不准确的地方,还希望各位大佬多多指教哈~
什么是状态压缩
状态压缩常和动态规划一起出现使用。字面意思可以理解为“减少要讨论的状态情况”。
对我来说,它就和动态规划一样是个很抽象、很宽泛的概念,脱离具体题目去讨论它似乎没什么意义。用状态压缩更多是一种“直觉”,就是这道题它涉及到了状态的讨论,然后你觉得可以用一种更简便、时间复杂度更低的写法去写。
划分为k个相同的子集
题目
给定一个整数数组
nums和一个正整数k,找出是否有可能把这个数组分成k个非空子集,其总和都相等。
思路
- 思路关键词:状态压缩+动态规划
- 对题意条件的分析:
- 特殊情况:如果nums数组的和不能整除k,说明不行
- 用 dp[i] 来表示在可用的数字状态为 i 的情况下是否可行,,dp数组z中的元素初始值都为true 。
- 通过 自顶向下 的方式来进行求解原始状态的可行性
- 当状态集合中不存在任何数字时,即 i=0 时,返回true
代码实现
class Solution {
int[] nums;
int per, n;
boolean[] dp;
public boolean canPartitionKSubsets(int[] nums, int k) {
this.nums = nums;
int all = Arrays.stream(nums).sum();
if (all % k != 0) {
return false;
}
per = all / k;
Arrays.sort(nums);
n = nums.length;
if (nums[n - 1] > per) {
return false;
}
dp = new boolean[1 << n];
Arrays.fill(dp, true);
return dfs((1 << n) - 1, 0);
}
public boolean dfs(int s, int p) {
if (s == 0) {
return true;
}
if (!dp[s]) {
return dp[s];
}
dp[s] = false;
for (int i = 0; i < n; i++) {
if (nums[i] + p > per) {
break;
}
if (((s >> i) & 1) != 0) {
if (dfs(s ^ (1 << i), (p + nums[i]) % per)) {
return true;
}
}
}
return false;
}
}