POJ - 3254 Corn Fields【状压dp模板】

状压dp就是将一个状态压缩成一个十进制数,再进行状态转移。一般是把一行数据按照合适的进制转化为一个整数。

1、一般来说需要先预处理出来所有可行的状态,判断可行可以用位运算对状态进行操作。

2、然后预处理出第一行的状态。

3、从第二行开始遍历每一行,枚举第i行的所有可行状态,枚举第i-1行的所有可行状态,判断其与本行是否冲突,状态间是否冲突。再进行转移。

对于本题,判断k状态是否与本行冲突,我们可以将本行的逆状态存起来,即0->1,1->0,然后!(state[k]&cur[i])确定k状态与第i行的逆状态没有相交。

题目链接:POJ - 3254 


代码:

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<math.h>
using namespace std;
#define ll long long
#define mod 100000000
int n,m,cur[15],state[1<<13],tot;//cur[i]存第i行的逆状态,state[i]存第i个可行的状态
int dp[15][1<<13];
void init()
{
    tot=0;
    memset(dp,0,sizeof dp);
    for(int i=0;i<(1<<m);i++)//m为列数
        if(!(i&(i<<1)))
            state[tot++]=i;
}
bool fit(int k,int t)
{
    return !(state[k]&cur[t]);//如果k状态与第t行的逆状态有重合,一定不合法
}
int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        for(int i=1;i<=n;i++)
        {
            int p=0;
            for(int j=0;j<m;j++)
            {
                int d;
                scanf("%d",&d);
                p=(p<<1)+!d;
            }
            cur[i]=p;
        }
        init();
        for(int i=0;i<tot;i++)
            if(fit(i,1))
                dp[1][i]=1;
        for(int i=2;i<=n;i++)
        {
            for(int j=0;j<tot;j++)//找出本行的状态
            {
                if(!fit(j,i)) continue;
                for(int k=0;k<tot;k++)//找出上一行的状态
                {
                    if(!fit(k,i-1)) continue;//匹配k状态与原图
                    if(state[j]&state[k]) continue;//匹配j状态与k状态
                    dp[i][j]=(dp[i][j]+dp[i-1][k])%mod;
                }
            }
        }
        int ans=0;
        for(int i=0;i<tot;i++)
            ans=(ans+dp[n][i])%mod;
        printf("%d\n",ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/u013852115/article/details/80148949