dp深入与进阶(3):状压dp(2025.02.03)
状压dp概念:
通过将状态压缩为整数(二进制数),从而优化状态转移的方法即为状压dp
而实际使用和操作时可以考虑位运算,还可以在一定程度上加快程序运行
优点:
1.简化复杂状态:用二进制压缩表示复杂状态,如 TSP 问题里城市访问状态
2.降低空间开销:相比传统记录方式,大幅减少状态存储的空间。
3.提升时间效率:利用位运算快速操作状态,减少计算量。
4.应用广泛:适用于棋盘布局、资源分配等多领域问题。
5.实现简便:基于二进制运算与 DP 框架,代码易理解与调试。
(
以上内容出自豆包)
状压dp的实现:Luogu P1896 [互不侵犯]
状态压缩
我们以行为单位,对于每一行放/不放王的状态,压缩为二进制01串
0
1
例:
对于上图,有状态
如此便可以用二进制数来存储所有的状态
条件判断
对于每次个王,
满足上、下、左、右、左上、左下、右上、右下八个方向上相邻的格子都不能有相邻的王
如何进行这样的判断呢?
我们假设第
((s1<<1)&s1==0)&&((s1>>1)&s1==0)
简写!((s1<<1)|(s1>>1)&s1)
((s1<<1)&s2==0)&&(s1&s2==0)&&((s1>>1)&s2==0)
简写!((s1<<1)|s1|(s1>>1)&s2)
可以把判断满足条件的语句封装为函数
状态转移
(1)状态设计:
考虑f[i][num][s]
表示第i行,使用num个棋子,当前状态为s的可行方案数
其中满足
(2)公式推导:
对于所有满足条件的
即
其中
初始化:
代码实现
#include<bits/stdc++.h> using namespace std; long long f[10][100][2010]; //判断s1,s2是否满足条件 bool sta(int s1,int s2) { if(((s1<<1)|(s1>>1))&s1) return 0; if(((s2<<1)|(s2>>1))&s2) return 0; if(((s1<<1)|s1|(s1>>1))&s2) return 0; return 1; } //计算s二进制中有多少个1 int get_num(int s) { int ret=0; while(s) { ret+=(s&1);//取二进制最后一位的1加入ret s>>=1;//s右移一位 } return ret; } int main() { int n,k; cin>>n>>k; f[0][0][0]=1; int maxn=1<<n; for(int i=1;i<=n;++i)//枚举当前行i for(int s1=0;s1<maxn;++s1)//枚举本行状态s1 for(int s2=0;s2<maxn;++s2)//枚举上一行状态s2 { if(sta(s1,s2)==0) continue; for(int num=0;num<=k;++num)//枚举放了的棋子数 { int num_s1=get_num(s1); if(num-num_s1>=0)//判断是否可以进行dp f[i][num][s1]+=f[i-1][num-num_s1][s2]; } } long long ans=0; for(int i=0;i<maxn;++i) ans+=f[n][k][i]; cout<<ans; return 0; } //这个题有更优化的思路 需要进行预处理 感兴趣的可以看XichenOC的题解 //代码:SamXia
这个题有另一个题解XiChenOC-状压DP(学习笔记)
来自XiChenOC大佬,里面有本题的优化技巧,可以进行学习理解
本文作者:SamXia
本文链接:https://www.cnblogs.com/SamXia/p/18695695
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步