POJ 3254 状压DP

来源:互联网 发布:java main sleep 编辑:程序博客网 时间:2024/05/24 04:23

题意

有一块草地,M行,N列。可以放任意头牛在上面吃草。要求牛不能相邻,且牛不能在0上吃草。求放置牛的方案数。

题解

非常好的一道题。用到了状态压缩的思想和很多数位操作技巧。首先整理出来每一行可能的状态status[]。然后dp[j][i]=(dp[j][i]+dp[k][i-1])%MOD。(这里需要解释一下,dp[j][i]代表状态号为j的情况下放置到第i行的方案数。k代表上一行的状态号。这里需要判断一下k&j是否==1,因为如果k&j==1代表有两头牛上下相邻,这是不可以的。)最后ans=(ans+dp[i][m])%MOD。

代码

#include <iostream>#include<cstdio>#include<algorithm>#include<cstring>#define MAXN 4100#define MOD 100000000using namespace std;int status[MAXN];int staNum;int mp[15];int dp[MAXN][15];int m,n;/*    技巧一:利用x&x<<1判断是否有相邻元素*/bool good(int x){    if(x&x<<1)        return false;    else        return true;}void init(){    staNum=0;    int mx=1<<n;    for(int i=0;i<mx;i++){        if(good(i))            status[++staNum]=i;    }}/*    技巧二:利用位运算判断是否在不该放置牛的位置上放置了牛*/bool fit(int x,int k){    if(x&mp[k]){        return false;    }else{        return true;    }}int main(){    while(~scanf("%d%d",&m,&n)){        memset(dp,0,sizeof(dp));        memset(status,0,sizeof(status));        memset(mp,0,sizeof(mp));        init();        for(int i=1;i<=m;i++){            for(int j=1;j<=n;j++){                int x;                scanf("%d",&x);                if(x==0)                    mp[i]+=1<<(n-j);            }        }        for(int i=1;i<=staNum;i++){            if(fit(status[i],1)){                dp[i][1]=1;            }        }        for(int i=1;i<=m;i++){            for(int j=1;j<=staNum;j++){                if(!fit(status[j],i)){                    continue;                }                for(int k=1;k<=staNum;k++){                    if(!fit(status[k],i-1))                        continue;                    if(status[j]&status[k])                        continue;                    dp[j][i]=(dp[j][i]+dp[k][i-1])%MOD;                }            }        }        int ans=0;        for(int i=1;i<=staNum;i++){            ans=(ans+dp[i][m])%MOD;        }        printf("%d\n",ans);    }    return 0;}
0 0