poj 3254 Corn Fields (状压DP)

题目大意: N*M的农场, 有的格子可以放牛,有的不行。在这块地方放牛,相邻的格子不能有牛。求方案数。

N、M都比较小(<13),那么可以用1个2进制位表示一行中的每一个格子的状态,0表示不放牛,1表示放牛,一行中的所有二进制位构成一个状态s。

设dp[i][s]表示第i行状态为s时的方案数。那么状态转移方程:

dp[i][s]=(dp[i1][s])

其中s’为i-1行的状态,且满足s和s’相同位不同时为1。

注意在枚举某一行i的状态的时候,有的状态是不合法的,可以通过奇妙的位运算来判别这些不合法的状态。比如判断状态s中是否存在两个相邻的1,判定s&(s>>1)是否为1即可。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef __int64 LL;
#define mod 100000000
#define maxn 1<<12

LL dp[12][maxn];
int cur[12];

inline bool check1(int i,int s)
{
    if((cur[i]&s)!=s) return 0;
    if(s&(s>>1)) return 0;
    return 1;
}

inline bool check2(int s1,int s2)
{
    if(s1&s2) return 0;
    return 1;
}

int main()
{
    int n,m,i,j,k,a,M;
    while(~scanf("%d%d",&n,&m)){
        memset(dp,0,sizeof(dp));
        M=(1<<m);
        for(i=0;i<n;++i){
            cur[i]=0;
            for(j=0;j<m;++j)
            {
                scanf("%d",&a);
                cur[i]=(cur[i]<<1)+a;
            }
        }
        for(j=0;j<M;++j) if(check1(0,j)) ++dp[0][j];
        for(i=1;i<n;++i)
            for(j=0;j<M;++j)
            {
                if(!check1(i,j)) continue;
                for(k=0;k<M;++k)
                {
                    if(!check2(j,k)) continue;
                    dp[i][j]+=dp[i-1][k];
                    if(dp[i][j]>mod) dp[i][j]-=mod;
                }
            }
        LL ans=0;
        for(i=0;i<M;++i) ans=(ans+dp[n-1][i])%mod;
        printf("%I64d\n",ans);
    }
    return 0;
}

你可能感兴趣的:(dp)