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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章