关于做状态压缩DP的一些经验


状压DP总是让我绝望(蒟蒻噩梦)
所以我就想写一篇博客来总结一发。(每道例题在我的博客中都可以找到完整题解)

NO.1

其实有时候并不需要把状态中每个状态给表示出来,只要不影响状态之间的转移即可。

比如:

[USACO08NOV]奶牛混合起来Mixed Up Cows(在洛谷的题目)
[USACO08NOV解题报告]奶牛混合起来Mixed Up Cows(蒟蒻的完整解题报告)

题目描述

约翰家有N头奶牛,第i头奶牛的编号是Si,每头奶牛的编号都是唯一的。这些奶牛最近 在闹脾气,为表达不满的情绪,她们在挤奶的时候一定要排成混乱的队伍。在一只混乱的队 伍中,相邻奶牛的编号之差均超过K。比如当K = 1时,1, 3, 5, 2, 6, 4就是一支混乱的队伍, 而1, 3, 6, 5, 2, 4不是,因为6和5只差1。请数一数,有多少种队形是混乱的呢?
(1<=n<=16)

解法:

这道题我当初就是死在试图把每头牛的位置表示出来
但经过一番摸♂索,我发现,其实不需要知道每头牛的位置。
只需知道每头牛是否已经被选和最后一牛的是第几只
因为如果用刷表法DP,每次状态状态转移只会转移合法状态,所以其实并不需要关注牛内部的排序方式。
dp[j][i]表示第j头牛是最后一只,选牛的方案用二进制表示为i。
主要代码

1
2
3
4
5
6
7
8
	for(int i=0;i<(1<<n);++i)//枚举状态
for(int j=0;j<n;++j) if(i&(1<<j))//枚举当前最后一只牛
for(int k=0;k<n;++k) if(!(i&(1<<k)) && abs(a[j]-a[k]) > K)
//枚举可以转移到新的最后一只牛的状态
dp[k][i|(1<<k)]+=dp[j][i];
for(int i=0;i<n;++i)//统计答案
ans+=dp[i][(1<<n)-1];
}

Author: BY 水蓝
Reprint policy: All articles in this blog are used except for special statements CC BY 4.0 reprint polocy. If reproduced, please indicate source BY 水蓝 !
  TOC