状压系列二。
题目大意:给定一个(n*m)的矩阵,其中有些位置能放,有些不能。规定一个格子放了则它的上下左右均不能放。问放格子的方案有多少种。
考虑到(n,m)很小,就像是状压了。
考虑状压的表示方法,我们把每一行的(01)矩阵压成二进制,表示这一行的状态。
那么我们压完行后,显然有(n)个数表示每一行的状态。那总状态数就是(2^n)。
同样,因为它左右不能放,那就先把这样合法的状态处理一下。
继续可以考虑(dp)了。
遍历每一行(压缩的状态),对于每一个状态枚举一下它可能对应的状态以及它下一行可能对应的状态。我们前面可以把合法的方案用布尔数组标记一下。
那么我们首先要选合法的状态。其次这种状态不能在题目中所给地图的禁区中种植。我们可以用(j&mp[i]==j)来判断是否合法。
那么接下来枚举下一行状态。显然上下两行不能有相邻的(1),(j&k)即可。
然后就是喜闻乐见的累加状态了。
题目做完了。注意判断左边和右边的时候,可以:
$ i&(i<<1)==0 $ (&&) (i&(i>>1)==0)
注意优先级,习惯加括号。
(Code:)
#include<iostream>
using namespace std;
int n,m,a[20][20],mp[20];
int dp[20][5000],Ans;
bool state[5000];
const int Hws=100000000;
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
scanf("%d",&a[i][j]);
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
mp[i]=(mp[i]<<1)+a[i][j];
for(int i=0;i<(1<<m);++i)if(((i&(i<<1))==0)&&((i&(i>>1)))==0)state[i]=1;
dp[0][0]=1;
for(int i=1;i<=n;++i){
for(int j=0;j<(1<<m);++j)
if(state[j]&&((j&mp[i])==j)){
for(int k=0;k<(1<<m);++k)
if((k&j)==0){
dp[i][j]+=dp[i-1][k];
dp[i][j]%=Hws;
}
}
}
for(int i=0;i<(1<<m);++i)Ans+=dp[n][i],Ans%=Hws;
printf("%d
",Ans);
return 0;
}