状压DP 详解教程 简单易学(bushi
状压DP补档
一、基本概念
- 什么是状压DP
状态压缩动态规划(State Compression Dynamic Programming)是一种通过二进制或其他紧凑表示方式来优化状态空间的动态规划方法。它通常用于解决状态可以表示为集合或排列的问题。
- 适用场景
状态可以表示为集合(如选/不选某些元素)
状态维度较高但每个维度状态较少(如棋盘覆盖问题)
需要记录访问历史或选择历史的问题
- 核心思想
用二进制数表示状态(0/1表示存在/不存在)
通过位运算高效地进行状态转移
将指数级的状态空间压缩为多项式级
二、常用位运算技巧
- 基本操作
// 设置第i位为1
mask |= (1 << i);
// 设置第i位为0
mask &= ~(1 << i);
// 切换第i位
mask ^= (1 << i);
// 检查第i位是否为1
if (mask & (1 << i)) {...}
// 获取最低位的1
lowbit = mask & -mask;
// 清除最低位的1
mask &= (mask - 1);
2. 高级操作
// 遍历所有子集
for (int subset = mask; subset; subset = (subset - 1) & mask) {
// 处理subset
}
// 检查mask是否是全1
if (mask == (1 << n) - 1) {...}
// 计算二进制中1的个数
int count = __builtin_popcount(mask); // GCC内置函数
三、经典问题模型
1. 旅行商问题(TSP)
状态表示:
DP[M][U]:已经访问过M表示的城市集合,当前位于城市U的最短路径
状态转移:
DP[M][U] = min(DP[M][U], DP[M^(1<<U)][V] + D[V][U])
2. 棋盘覆盖问题
状态表示:
- 用二进制表示一行的覆盖状态
- 可能需要多行状态共同表示
四、解题模板
1. 基本框架(ALLman风格)
int DP[1 << N][...]; // 状态数组
memset(DP, INF, sizeof(DP)); // 初始化
// 初始状态
DP[INIT_M][...] = INIT_VAL;
// 状态转移
for (int M = 0; M < (1 << N); ++M)
{
for (int I = 0; I < N; ++I)
{
if (!(M & (1 << I))) // 如果第I位未被选中
{
int NEW_M = M | (1 << I);
DP[NEW_M][...] = UPDATE(DP[M][...], ...);
}
}
}
// 结果通常是DP[FULL_M][...]
2. 位运算操作(ALLman风格)
// 设置第I位为1
M |= (1 << I);
// 设置第I位为0
M &= ~(1 << I);
// 切换第I位
M ^= (1 << I);
// 检查第I位是否为1
if (M & (1 << I)))
{
...
}
// 获取最低位的1
LB = M & -M;
// 清除最低位的1
M &= (M - 1);
五、例题精选
1. Leetcode 464. 我能赢吗
bool CAN_I_WIN(int MAX_CHOOSE, int TARGET)
{
if (MAX_CHOOSE >= TARGET)
{
return true;
}
if (MAX_CHOOSE * (MAX_CHOOSE + 1) / 2 < TARGET)
{
return false;
}
unordered_map<int, bool> MEMO;
return DFS(0, 0, MAX_CHOOSE, TARGET, MEMO);
}
bool DFS(int USED, int SUM, int MAX_CHOOSE, int TARGET, unordered_map<int, bool>& MEMO)
{
if (MEMO.count(USED))
{
return MEMO[USED];
}
for (int I = 1; I <= MAX_CHOOSE; ++I)
{
if (!(USED & (1 << I)))
{
if (SUM + I >= TARGET || !DFS(USED | (1 << I), SUM + I, MAX_CHOOSE, TARGET, MEMO))
{
return MEMO[USED] = true;
}
}
}
return MEMO[USED] = false;
}
2. Leetcode 691. 贴纸拼词
int MIN_STICKERS(vector<string>& STICKERS, string TARGET)
{
int N = TARGET.size();
vector<int> DP(1 << N, -1);
DP[0] = 0;
for (int M = 0; M < (1 << N); ++M)
{
if (DP[M] == -1)
{
continue;
}
for (string& S : STICKERS)
{
int CUR = M;
for (char C : S)
{
for (int I = 0; I < N; ++I)
{
if (!(CUR & (1 << I)) && C == TARGET[I])
{
CUR |= (1 << I);
break;
}
}
}
if (DP[CUR] == -1 || DP[CUR] > DP[M] + 1)
{
DP[CUR] = DP[M] + 1;
}
}
}
return DP[(1 << N) - 1];
}
3. Leetcode 1434. 戴帽子
int NUMBER_WAYS(vector<vector<int>>& HATS)
{
const int MOD = 1e9 + 7;
int N = HATS.size();
vector<vector<int>> DP(41, vector<int>(1 << N, 0));
DP[0][0] = 1;
for (int I = 1; I <= 40; ++I)
{
for (int M = 0; M < (1 << N); ++M)
{
DP[I][M] = DP[I - 1][M];
}
for (int P = 0; P < N; ++P)
{
if (find(HATS[P].begin(), HATS[P].end(), I) != HATS[P].end())
{
for (int M = 0; M < (1 << N); ++M)
{
if (!(M & (1 << P)))
{
int NEW_M = M | (1 << P);
DP[I][NEW_M] = (DP[I][NEW_M] + DP[I - 1][M]) % MOD;
}
}
}
}
}
return DP[40][(1 << N) - 1];
}
886
状压DP 详解教程 简单易学(bushi的更多相关文章
- 状态压缩dp 状压dp 详解
说到状压dp,一般和二进制少不了关系(还常和博弈论结合起来考,这个坑我挖了还没填qwq),二进制是个好东西啊,所以二进制的各种运算是前置知识,不了解的话走下面链接进百度百科 https://baike ...
- 状态压缩动态规划(状压DP)详解
0 引子 不要999,也不要888,只要288,只要288,状压DP带回家.你买不了上当,买不了欺骗.它可以当搜索,也可以卡常数,还可以装B,方式多样,随心搭配,自由多变,一定符合你的口味! 在计算机 ...
- 状压DP详解(位运算)
前言: 状压DP是一种非常暴力的做法(有一些可以排除某些状态的除外),例如dp[S][v]中,S可以代表已经访问过的顶点的集合,v可以代表当前所在的顶点为v.S代表的就是一种状态(二进制表示),比如 ...
- 状压DP详解+题目
介绍 状压dp其实就是将状态压缩成2进制来保存 其特征就是看起来有点像搜索,每个格子的状态只有1或0 ,是另一类非常典型的动态规划 举个例子:有一个大小为n*n的农田,我们可以在任意处种田,现在来描述 ...
- 状压DP初探·总结
2018过农历新年这几天,学了一下状态压缩动态规划,现在先总结一下. 状态压缩其实是一种并没有改变dp本质的优化方法,阶段还是要照分,状态还是老样子,决策依旧要做,转移方程还是得列,最优还是最优, ...
- luoguP6622 [省选联考 2020 A/B 卷] 信号传递(状压dp)
luoguP6622 [省选联考 2020 A/B 卷] 信号传递(状压dp) Luogu 题外话: 我可能是傻逼, 但不管我是不是傻逼, 我永远单挑出题人. 题解时间 看数据范围可以确定状压dp. ...
- 状压DP入门详解+题目推荐
在动态规划的题型中,一般叫什么DP就是怎么DP,状压DP也不例外 所谓状态压缩,一般是通过用01串表示状态,充分利用二进制数的特性,简化计算难度.举个例子,在棋盘上摆放棋子的题目中,我们可以用1表示当 ...
- poj2411 Mondriaan's Dream[简单状压dp]
$11*11$格子板上铺$1*2$地砖方案.以前做过?权当复习算了,毕竟以前学都是浅尝辄止的..常规题,注意两个条件:上一行铺竖着的则这一行同一位一定要铺上竖的,这一行单独铺横的要求枚举集合中出现连续 ...
- 简单状压dp的思考 - 最大独立集问题和最大团问题 - 壹
本文参考:CPH ,USACO Guide (大佬请越过,这是初学笔记,不要吐槽内容) 前置知识:位运算基础,动态规划基础 介绍 状态是元素的子集的动态规划算法,可以用位运算来高效的优化. 那么第一道 ...
- POJ 3254 简单状压DP
没什么可说的,入门级状压DP.直接撸掉 #include <iostream> #include <cstring> #include <cstdlib> #inc ...
随机推荐
- ChatMoney化身恋爱大师来帮助你收获完美爱情!
本文由 ChatMoney团队出品 介绍说明 在纷繁复杂的情感世界里,寻找真爱的道路常常充满迷茫和困惑.但现在,有了 AI 智能体恋爱大师,为您的爱情之旅点亮明灯. AI 智能体恋爱大师并非传统意义上 ...
- 禁止后退Js 兼容各个浏览器
<script src="${ctxPath}/media/lib/jquery.history.js"></script> <script> ...
- 翻译 | 阿里巴巴的Dapr实践与探索
本文原文来自Dapr Blog,作者阿里云高级技术专家敖小剑.本文是我根据自己的理解翻译了其中的一部分并加了一些参考文献中的内容,所以并非完整翻译,点击此处即可阅读原版英文全文. 1 关于Dapr D ...
- SS 集训做题
9.11 主题:分块 9. 主题:计数 Problem A 矩乘板子 Problem B 观察到 N 很小.记 \(f_{i, S}\) 为第 \(i\) 列状态为 \(S\) 的方案数,\((2^8 ...
- Stack Overflow,轰然倒下!
你好呀,我是歪歪. 前几天看到一个让我感慨万千的走势图: 本来想让你猜一猜这个走势图的内容是什么的. 但是结合标题你应该也能猜到了,和 Stack Overflow 有关. 这个走势图的数据是 Sta ...
- 使用远程IO控制器ZLAN6808-3代替PLC实现电池工业生产线自动化
动力电池是一种能量储存装置,随着智能制造在传统制造领域的进一步渗透,作为国家战略性新兴产业,明确提出动力电池产业的智能制造发展规划,动力电池的生产已逐步从半自动化.自动化向智能化.数字化转型. 在动力 ...
- teamcity自动化部署
简介 用的自动化部署的工具,IntelliJ 家的产品teamcity对内存要求及高,我的1gb的内存就出现了"TeamCity服务器正在遇到内存不足的问题.内存清理花费了超过50%的时间. ...
- 好用的浏览器自动化扩展-automa
好用的浏览器自动化扩展-automa 转载请注明出处: https://www.cnblogs.com/funnyzpc/p/18932812 automa是一款基于浏览器的任务流编排工具,就是处理一 ...
- 记录一次Armbian安装宝塔面板遇到ModuleNotFoundError: No module named '_sqlite3'的问题
如果在用Armbian安装宝塔面板的时候遇到ModuleNotFoundError: No module named '_sqlite3'报错,并且无法进入web面板界面,可以尝试以下操作. 报错界面 ...
- iga 入门之 总体合成
简介 摘自 流体力学数值方法 概括地说,总体合成就是将所有单元的\(A_{ij}^{(e)}.f_i^{(e)}\)进行累加,最终形成\(A_{nm}.f_n\),从而产生总体有限元方程 \[A_{n ...