【洛谷 1879】玉米田
题目描述
农场主John新买了一块长方形的新牧场,这块牧场被划分成M行N列(1 ≤ M ≤ 12; 1 ≤ N ≤ 12),每一格都是一块正方形的土地。John打算在牧场上的某几格里种上美味的草,供他的奶牛们享用。
遗憾的是,有些土地相当贫瘠,不能用来种草。并且,奶牛们喜欢独占一块草地的感觉,于是John不会选择两块相邻的土地,也就是说,没有哪两块草地有公共边。
John想知道,如果不考虑草地的总块数,那么,一共有多少种种植方案可供他选择?(当然,把新牧场完全荒废也是一种方案)
输入格式:
第一行:两个整数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
解析:
1.本题是个经典的状态压缩DP题
2.设f[i][j]表示从首行到第i行且第i行状态为j的方案数(j表示的是二进制转化为十进制的数,从00…0到11…1的数[m个0或1]。每一个0或者1表示种了玉米和没种玉米)
3.如果第i行和第i-1行没有冲突,且第i行左边没有相邻的1且满足贫瘠的土地上不种玉米,则f[i][j]+=f[i-1][k],j和k都表示(二进制)状态
4.如何判断j里面有没有存在相邻的1
?。如果s&(s<<1)>0则有,=0则无。例如1101左移一位变成11010与01101相与结果是01000。有相邻的1.但是例如1001左移一位变成10010与01001相与结果是00000。没有相邻的1.
5.如何判断第i行和第i-1行没有冲突?如果第i行的二进制是j,i-1行的二进制是k,则判断j&k是否等于0就行了,如果等于0则不冲突,若不等于0则冲突。因为不等于0的时候必须有一对对应位两个都是1
6.如何判断贫瘠的土地不种玉米?把每行的a[i][j]转换成二进制存到另一个F[i]数组里面去。然后判断的时候只要((j&F[i])==j)则满足条件。如果都为1,满足条件!j不会改变还是1。若都为0,满足条件!j不会改变还是0。如果是j的第k位的二进制值是1,而F[i]的第k位的二进制值为0,这时,在贫瘠的土地上种了玉米,不符合条件!此时,j会改变成0。
7.注意细节!f[13][4100],F[4100]因为数字可以开到2的12次方4096。[查了好久才发现,为什么会WA呢……再说了数组开大不花钱(划掉)]
8.我打字好累的!!!
代码如下:
#include<cstdio> #include<iostream> #include<cstdlib> #include<cstring> #include<algorithm> #include<cmath> using namespace std; const int Mod=100000000; int m,n,f[13][4100],F[4100],a[13][13],ans; int main(){ //freopen("a3254.in","r",stdin); //freopen("a3254.out","w",stdout); 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++) F[i]=(F[i]<<1)+a[i][j]; //把数组a[i][j]转换到F[i]二进制里面去 int se=1<<m;//m位的二进制 f[0][0]=1; for(int i=1;i<=n;i++) for(int j=0;j<se;j++) if(((j&(j<<1))==0) && ((j&F[i])==j)) //如果左边没有相邻的1且满足土地条件 for(int k=0;k<se;k++) if((j&k)==0)//i行的j状态的i-1行的k状态不冲突 f[i][j]=(f[i][j]+f[i-1][k])%Mod;//更新 for(int i=0;i<se;i++) ans=(ans+f[n][i])%Mod;//加上答案 printf("%d",ans); return 0; }