DP 状压 - wenzhoullq/leetcode GitHub Wiki
分析
状压的n的范围是(1,20),本质上就是对子集的划分和子集的枚举
一般状压优先考虑dp,dfs记忆化很可能记忆化漏掉,不推荐
状压的时间复杂度分析:
n = arr.length;
for(int i = 0 ; i < (1<<n) ; i++){
for(int j = i ; j > 0 ; j = (j-1)&i)
}
该时间复杂度为 3^n
当n = 8 时, 随便枚举
当n = 16 时,时间复杂度为4e7,在lc里java的时间复杂度为1e9,妥妥够用,而py则是4e7,需要做优化
常见位运算:
j&-j 取最低位的1
枚举子集即可
int n = nums.length, u = 1 << n;
for(int i = 0; i < u ; i++){
for(int j = 0; j < n ; j++){
if(((1<<j)&i)==0) continue;
}
}
时间复杂度O(2^n * n)
K次循环和状压
如果出现一个 0 < cnt < 100 和一个 0< n < 16的,考虑这种枚举
//可以考虑预处理
dp[0] = 0;
int n ;//n是被状压的对象
int u = 1<<n;//u是全集
for(int i = 0 ; i < k ; i++){
for(int j = u - 1; j > 0 ; j--){//这种是枚举非空真子集,如果涉及到空集合,需要 int j = 0 ; j < u ; j++
for(int s = j ; s > 0; s = (s-1)&j){//枚举j的子集
}
}
}
时间复杂度为O(k * 3^n)
通过滚动数组,从j = u - 1 开始枚举可以节约空间
空间复杂度为O(1<<n)
- 题目
1434. 每个人戴不同帽子的方案数 变体,把帽子的枚举变成K
1125. 最小的必要团队 不一样的点在于,不是通过 j^s , j&~s,进行枚举子集
1986. 完成任务的最少工作时间段 k = 1的
枚举数组和arr数组匹配
存在俩数组,一个是枚举数组,一个是用于匹配的数组
无需考虑匹配数组的实际是怎么分布的,只需考虑Integer.bitCount(i) 表明它是多少,因为在枚举的过程中会把所有的都会 i - > 匹配数组 匹配一遍
考虑清楚哪个是枚举数组(状压谁)
int[] arr ;//匹配数组
int n ;//被状压的对象
int u = (1<<n) ;
int[] dp = new int[u];
for(int i = 0 ; i < u ; i++){
int index = Integer.bitCount(i);
for(int j = 0 ; j < n ; j++){
if(((1<<j)&i)==0) continue;
dp[i] = Math.(dp[i],dp[i^(1<<j)] + arr[index-1] and 题目具体的计算);
}
}
return dp[u-1];//有时候答案并非dp[u-1],需要引入一个ans
时间复杂度O(2^n * n) 空间复杂度O(n)
题目
1799. N 次操作后的最大分数和 加一个预处理
- 折半状压
不是很懂,下次再说