版权声明:我这么弱的蒟蒻,虽然博文不是很好,但也请标明转发地址喵! https://blog.csdn.net/ACerAndAKer/article/details/82805921
状压DP入门题
我写的第一道状压DP
状压就是把某种状态,用某种进制的数字串表示出来,比如说一行格子,奇数格子有 障碍物,偶数没有,我们就可以表示成1010101,这大概就是状压的主要思想
对于这些数字串,我们叫做可行状态,需要预处理出来,然后我们发现,预处理所有状态,可以用dfs搜一遍,就可以很快的得到所有可行状态
对于这道题,可行状态就是在二进制表示中两个1不挨着,如何知道二进制表示不挨着?将串向左移动一位,把每位错开,按位&一下,是0则没有重叠部分,就是可行解,然后我们预处理一下在m个格子内有多少种可行状态
if((i&(i<<1))==0) s[++cnt]=i
然后就是对初始状态的预处理,定义f[i][j]表示第i行的状态为j的方案数,我们先预处理第一行可行的状态k,然后f[i][k]=1,如何确定是否可行?因为所有可行状态都已经处理完了,所以每行可行的唯一条件就是不用有0的 位置,所以我们按位与一下,我们先预处理了每行的什么位置有0,然后按位与的话,可行就会是0,然后就没了
最后就是转移,每一行的状态只与上一行有关,所以只要和上一层的状态按位与是0就说明没有上下相邻的方格,所以我们枚举当前行遇上一行的状态,对于可行状态转移一下就好了
代码
//By AcerMo
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int mod=1e8;
int n,m;
int cnt,s[2050],e[20],f[20][2050];
inline int read()
{
int x=0;char ch=getchar();
while (ch>'9'||ch<'0') ch=getchar();
while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x;
}
inline void dfs()
{
int su=1<<m;
for (int i=0;i<su;i++)
if (!(i&(i<<1))) s[++cnt]=i;
return ;
}
inline bool che(int x,int y)
{
return (s[x]&e[y])==0;
}
signed main()
{
n=read();m=read();dfs();
for (int i=1;i<=n;i++)
for (int k=1;k<=m;k++)
{
int x=read();
if (!x) e[i]+=(1<<(m-k));
}
for (int i=1;i<=cnt;i++)
if (che(i,1)) f[1][i]=1;
for (int i=2;i<=n;i++)
for (int k=1;k<=cnt;k++)
if (che(k,i))
for (int j=1;j<=cnt;j++)
if (che(j,i-1)&&(s[j]&s[k])==0)
f[i][k]=(f[i][k]+f[i-1][j])%mod;
int ans=0;
for (int i=1;i<=cnt;i++)
ans=(ans+f[n][i])%mod;
cout<<ans;
return 0;
}