做了这一题,大概明白状态压缩是怎么回事了。。。 参考了牛人的代码~代码涉及的位运算不多,还好理解,有些大牛用来了很多位运算技巧,直接看不懂
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])
3.dp[i][j] = SUM(dp[i-1][k]),其中state[k]+state[j]!=(state[k]|state[j]),即上下相邻位置不放牛
由此,可以求出所有的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);
}