做了这一题,大概明白状态压缩是怎么回事了。。。 参考了牛人的代码~代码涉及的位运算不多,还好理解,有些大牛用来了很多位运算技巧,直接看不懂
poj 3254 Corn Fields http://poj.org/problem?id=3254
题意:
输入m行n列的数字,其中为1或者是0,1表示土壤肥沃可以种植草地,0则不可以。在种草地的区域可以放牛,但相邻的四个区域不允许同时放牛,问有多少种放牛的方法?
分析:
由m,n<=12,可用状态压缩
对于第i行,可以放草的格子置为0,不可以种草的格子设置为1,整一行的状态存入map[i]中(与题目中给出的相反,后面解释)
对于每一行,放牛的格为1,不放牛的格为0,整行用一个二进制数表示
dp[i][j]表示第i行放牛状态为j时有多少种方法,易知:
1.首先j必须合法,即左右相邻两位不同时出现1,运行时可以先选取合法的状态加入数组
void init() { for(int i=0; i<(1<<m); i++) //舍去存在连续是1的状态 { int temp=i<<1; if(i&temp) continue; state[++cnt]=i;//各种放牛的状态 } }2.不能在不能种草的地方放牛,即 map[i]+state[j]!=(state[j]|map[i])
由此,可以求出所有的dp[i][j],那么放牛的种类共有 = SUM(dp[n-1][j])最后一行所有状态的放牛种类之和
//AC CODE:
#include<iostream> #include<cmath> #include<algorithm> #include<vector> #include<cstdio> #include<cstdlib> #include<cstring> #include<string> using namespace std; #define MOD 100000000 int dp[13][3000]; int n,m,map[12],cnt; int state[3000]; void init() { for(int i=0; i<(1<<m); i++) //舍去存在连续是1的状态 { int temp=i<<1; if(i&temp) continue; state[++cnt]=i;//各种放牛的状态 } } int main() { int i,j,x,k; scanf("%d%d",&n,&m); cnt=0; init(); memset(map,0,sizeof(map)); memset(dp,0,sizeof(dp)); for(i=0; i<n; i++) for(j=0; j<m; j++) { scanf("%d",&x); map[i]=map[i]<<1;//map[i]中的二进制压缩,1表示土壤贫瘠,0表示肥沃,正好和题中说的相反 map[i]+=(x^1); } //初始化第一行 for(i=1; i<=cnt; i++) //放牛的格为1,不放牛的格为0,1表示土壤贫瘠,0表示肥沃 if(map[0]+state[i]==(state[i]|map[0])) dp[0][i]=1; //初始化其它行 for(i=1; i<n; i++) for(j=1; j<=cnt; j++) { if(map[i]+state[j]!=(state[j]|map[i]))//该状态不存在 continue; for(k=1; k<=cnt; k++)//该状态存在 +上一行的值 { if(state[k]+state[j]!=(state[k]|state[j])) continue; //上一行草与该行类似,有草的地方有,米草的地方也有 比如:草:0101 牛:1010 上一行:0000 dp[i][j]+=dp[i-1][k]; if(dp[i][j]>=MOD) dp[i][j]-=MOD; } } //计算结果 int sum=0; for(i=1; i<=cnt; i++) { sum+=dp[n-1][i]; if(sum>=MOD) sum-=MOD; } printf("%d\n",sum); }