专栏文章

状压DP总结

个人记录参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@minhtz63
此快照首次捕获于
2025/12/02 02:39
3 个月前
此快照最后确认于
2025/12/02 02:39
3 个月前
查看原文

状压DP入门详解:从思路到实战

一、什么是状压DP?

状态压缩动态规划(简称状压DP)是一种利用二进制位运算来表示和处理状态的动态规划方法。它主要解决的是状态过多但规模较小的问题,通常适用于数据范围在 n ≤ 25 的情况。

为什么需要状压?

在传统的DP中,如果状态是某个集合,我们很难直接用一个数组维度来表示。而二进制恰好可以很自然地表示"选"或"不选"两种状态,多个二进制位组合就能表示一个集合。

二、状压DP的核心——位运算

必备的位运算操作

操作代码含义
判断第i位是否为1(j>>i)&1检查状态j中第i个元素是否被选中
将第i位设为1`S=(1<<i)`
检查相邻1j&(j>>1)检查状态j中是否有相邻的1
取交集S&T两个状态取交集
取并集STS或T两个状态取并集

三、状压DP的通用解题框架

解题四步法:

  1. 状态设计
    • 定义dp[j]dp[j]dp[i][j]dp[i][j],其中jj是二进制状态
    • 明确状态含义:dp[j]dp[j]表示达到状态S的答案(方案数/最小代价等)
  2. 状态转移
    • 考虑如何从已知状态推导到未知状态
    • 通常形式:dp[新状态]=min/max(dp[新状态],dp[原状态]+代价)dp[新状态]=min/max(dp[新状态], dp[原状态]+代价)
    • 或者:dp[新状态]+=dp[原状态]dp[新状态]+=dp[原状态]
  3. 初始化
    • 确定起点状态的初始值
    • 通常:dp[0]=0dp[0]=0dp[初始状态]=1dp[初始状态]=1
  4. 结果提取
    • 从最终状态中提取答案
    • 可能是dp[全集]dp[全集]min(dp[所有状态])min(dp[所有状态])

四、经典例题详解

例题1:P1879 Corn Fields G

题目大意:在m×nm×n的土地上种草,有些格子不能种,相邻格子不能同时种草,求方案数。
CPP
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m,a[2005],mod=1e8,dp[25][1<<13];
signed main()
{
	cin>>m>>n;
	for(int i=1;i<=m;i++)
	{
		for(int j=1;j<=n;j++)
		{
			int x;
			cin>>x;
			a[i]=(a[i]<<1)+x;
		}
	}
	dp[0][0]=1;
	for(int i=1;i<=m;i++)
	{
		for(int j=0;j<(1<<n);j++)
		{
			if((j&(j<<1))==0)
			{
				if((j&a[i])==j)
				{
					for(int k=0;k<(1<<n);k++)
					{
						if((j&k)==0)
						{
							dp[i][j]=(dp[i][j]+dp[i-1][k])%mod;
						}
					}
				}
			}
		}
	}
	int ans=0;
	for(int i=0;i<(1<<n);i++)
		ans=(ans+dp[m][i])%mod;
	cout<<ans;
	return 0;
} 
关键点
  • a[i]a[i]存储每行的土地状态,1表示肥沃
  • 预处理所有行内不相邻的状态
  • 转移时检查:①状态与土地匹配 ②上下行不相邻

五、状压DP的常见题型总结

题型特征解题关键
棋盘放置棋盘上有放置限制预处理行内合法状态,检查行间兼容性
最短路径需要访问所有点一次状态表示已访问的点集,维度记录当前位置
集合划分将集合划分成若干子集状态表示已分配的元素集合

六、实战技巧与注意事项

1. 空间优化

  • n=20n=20时,状态数可达2201e62^20≈1e6,要注意数组开不下时使用滚动数组

2. 时间优化

  • 预处理合法状态和状态间关系
  • 使用剪枝减少不必要的状态转移

3. 调试技巧

CPP
// 打印二进制状态(调试用)
void print(int s,int n) {
    for(int i=n-1;i>=0;i--)
    {
        cout<<((s>>i)&1);
    }
    cout<<endl;
}

4. 常见错误

  • 位运算优先级问题:多用括号,如(s >> i) & 1
  • 数组越界:注意状态范围是0(1<<n)-1
  • 初始化不完整:确保所有起点状态正确初始化

七、推荐练习顺序

  1. P1879 Corn Fields G - 最基础的棋盘状压DP
  2. P1171 售货员难题 - 经典的旅行商问题
  3. P1433 吃奶酪 - 二维平面上的最短路径

评论

0 条评论,欢迎与作者交流。

正在加载评论...