状态压缩DP
DestinHistoire · · 个人记录
二进制状态压缩
二进制状态压缩,是指将一个长度为
这种方法运算简便,并且节省了程序运行的时间和空间。当
【例题】最短Hamilton路径 CH0103
给定一张
Hamilton路径的定义是从
【分析】
很容易想到本题的一种朴素做法,就是枚举
在任意时刻如何表示哪些点已经被经过,哪些点没有被经过?可以使用一个
在起点时,有
在任意时刻,有公式
最需要注意的地方是:大小关系比较的符号优先于 “位与” “异或” “位或” 运算。 在程序实现时,如果不确定优先级,建议加括号保证运算顺序的正确性。
【代码】
#include<bits/stdc++.h>
using namespace std;
int f[1<<20][20],mp[20][20],n;
int hamilton(int n,int mp[20][20])
{
memset(f,0x3f,sizeof(f));
f[1][0]=0;
for(int i=1;i<(1<<n);i++)
for(int j=0;j<n;j++)
if(i>>j&1)
for(int k=0;k<n;k++)
if((i^1<<j)>>k&1)
f[i][j]= min(f[i][j],f[i^1<<j][k]+mp[k][j]);
return f[(1<<n)-1][n-1];
}
int main()
{
cin>>n;
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
cin>>mp[i][j];
cout<<hamilton(n,mp)<<endl;
return 0;
}
状态压缩DP
动态规划的过程是随着“阶段”的增长,在每个状态维度上不断扩展的。在任意时刻,已经求出最优解的状态与尚未求出最优解的状态在各维度上的分界点组成了DP扩展的“轮廓”。对于某些问题,我们需要在动态规划的“状态”中记录一个集合,保存这个“轮廓”的详细信息,以便进行状态转移。若集合大小不超过
上题已经展示了简单的状态压缩DP思想。在求解最短 Hamilton 路径时,整个状态空间可看作
【例题】 Mondriaan's Dream POJ-2411
求把
【分析】
对于任意一种方案,考虑以某一行为界,把整个棋盘横着分成两半,如上图所示。在上半部分最后一行中:
综上所述,我们可以把“行号”作为DP的“阶段”,把“上半部分”不断向下扩展,直至确定整个棋盘的分割方法。为了描述上半部分最后一行的详细形态,我们可以使用一个
设
第
这保证了每个数字
这些
可以在DP前预处理出
边界条件:
目标:
时间复杂度:
【代码】
#include<bits/stdc++.h>
using namespace std;
int n,m;
long long f[12][1<<11];
bool in_s[1<<11];
int main()
{
while(cin>>n>>m&&n)
{
for(int i=0;i<1<<m;i++)
{
bool cnt=0,has_odd=0;
for(int j=0;j<m;j++)
{
if(i>>j&1)
has_odd=has_odd|cnt,cnt=0;
else
cnt=cnt^1;
in_s[i]=has_odd|cnt?0:1;
}
f[0][0]=1;
for(int i=1;i<=n;i++)
{
for(int j=0;j<1<<m;j++)
{
f[i][j]=0;
for(int k=0;k<1<<m;k++)
if((j&k)==0&&in_s[j|k])
f[i][j]=f[i][j]+f[i-1][k];
}
}
}
cout<<f[n][0]<<endl;
}
return 0;
}