题目描述
农夫有一块地,被划分为m行n列大小相等的格子,其中一些格子是可以放牧的(用1标记),农夫可以在这些格子里放牛,其他格子则不能放牛(用0标记),并且要求不可以使相邻格子都有牛。现在输入数据给出这块地的大小及可否放牧的情况,求该农夫有多少种放牧方案可以选择(注意:任何格子都不放也是一种选择,不要忘记考虑!
输入
第一行:两个整数M和N,用空格隔开。
第2到第M+1行:每行包含N个用空格隔开的整数,描述了每块土地的状态。第i+1行描述了第i行的土地,所有整数均为0或1,是1的话,表示这块土地足够肥沃,0则表示这块土地不适合种草。
输出
一个整数,即牧场分配总方案数除以100,000,000的余数。
样例输入
2 3 1 1 1 0 1 0
样例输出
9
题解
第一行有5种放法: 0 0 0,0 0 1,0 1 0,1 0 0,1 0 1 ,分别对应10进制数 0,1,2,4,5 , 于是我们就把状态压缩成了一个数,这就是状态压缩。
如果我们知道一排的信息,那么我们可以枚举出下一排的信息,即当前这一排的牛放不放只与上一排怎么放和这一排本身能不能放有关系。
用state表示每一行的情况,如state=1,则对应 0 0 1。由于一行里每个位置有两种选择,所以state的取之范围应该是 [0 ~ 1<< n)。
如何判断state是否可行?例如state=3,对应状态 0 1 1,显然不行,我们可以得出判断方法:若当前状态state=x,如果 x&x<<1 ==true ,那么此状态不合法。有了这个方法,我们可以先预处理出所有可行的 state[ i ]。
转移:设 dp[ i ][ state[ j ] ] 表示对于前 i 行,第 i 行状态为 state[ j ] 时的方案总数,那么 dp[ i ][ state[ j ] ] = dp[ i-1 ][ state[ k1 ] ] + dp[ i-1 ][ state[ k2 ] ] + ...... + dp[ i-1 ][ state[ kn ] ] ( kn即为上一行可行状态的编号,上一行共有n种可行状态 )
这里又说到了可行状态,那如何判断上一行的方案是否可行?设 state[ i ] 为本行状态,state[ j ] 为上一行状态,如果 state[ i ]&state[ j ] == true ,则上一行状态不合法。
前面说过,这一排的牛放不放只与上一排怎么放和这一排本身能不能放有关系,如何判断当前状态state[ i ] 能不能放在第 i 行?将第 i 行能放牛的地方记为0,不能放的为1,得到10进制数cur[ i ],如果state[ i ]&cur[ i ] ==true ,说明这行的条件不允许state[ i ]这种状态出现。
#include<cmath> #include<cstdio> #include<cstdlib> #include<cstring> #include<iostream> #include<algorithm> using namespace std; #define ll long long const int mod=100000000; int state[600],num[110],top; int dp[20][600],cur[20],n,m; bool ok(int x){ if(x&x<<1) return false; return true; } void init(){ top=0; for(int i=0;i<(1<<n);i++) if(ok(i)) state[++top]=i; } bool fit(int x,int k){ if(x&cur[k]) return false; return true; } template<typename T>void read(T& aa){ char cc; ll ff;aa=0;cc=getchar();ff=1; while((cc<'0'||cc>'9')&&cc!='-') cc=getchar(); if(cc=='-') ff=-1,cc=getchar(); while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar(); aa*=ff; } int main(){ while(scanf("%d%d",&m,&n)!=EOF){ init(); memset(dp,0,sizeof(dp)); for(int i=1;i<=m;i++){ cur[i]=0; int num; for(int j=1;j<=n;j++){ read(num); if(num==0) cur[i]+=(1<<(n-j)); } } for(int i=1;i<=top;i++) if(fit(state[i],1)) dp[1][i]=1; for(int i=2;i<=m;i++) for(int k=1;k<=top;k++){ if(!fit(state[k],i)) continue; for(int j=1;j<=top;j++){ if(!fit(state[j],i-1)) continue; if(state[k]&state[j]) continue; dp[i][k]=(dp[i][k]+dp[i-1][j])%mod; } } int ans=0; for(int i=1;i<=top;i++) ans=(ans+dp[m][i])%mod; cout<<ans<<endl; } return 0; }