P1879 [USACO06NOV]玉米田Corn Fields 【状压dp模板】

gate

前置知识

位运算常用方法

判断一个数字x二进制下第i位是不是1

  • return ((1<<(i1))&x)?true:false

将一个数字x二进制下第i位更改成1。

  • x=x|(1<<(i1))

把一个数字二进制下最靠右的第一个1去掉。

  • x=x&(x1)

判断一个数是否为2的幂

  • return x & (x-1) == 0 

找到最右边的1(也就是lowbit)

  • return x & (-x)

直接看代码

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#define MogeKo qwq
using namespace std;

const int mod = 1e9;

int n,m,x,ans;
int a[13],f[13][1<<12+5],g[1<<12+5];

int main() {
    scanf("%d%d",&m,&n);
    for(int i = 1; i <= m; i++)
        for(int j = 1; j <= n; j++) {
            scanf("%d",&x);
            a[i] = (a[i]<<1)|x;
        }

    for(int i = 0; i < (1<<n); i++)
        g[i] = (!((i<<1)&i) && !((i>>1)&i));
        
    f[0][0] = 1;
    for(int i = 1; i <= m; i++)
        for(int j = 0; j < (1<<n); j++)
        if(g[j] && ((j&a[i]) == j))
            for(int k = 0; k < (1<<n); k++)
            if((k & j) == 0)
                    f[i][j] = (f[i][j] + f[i-1][k])%mod;
    for(int i = 0; i < (1<<n); i++)
        ans = (ans + f[m][i])%mod;
    printf("%d\n",ans);
    return 0;
}

 

限制条件:

  1. 土地本身可用
  2. 左右不相邻
  3. 上下不相邻

首先读入每一格土地状态,对于第i行,用a[i]表示每一块土地是否可用;

a[i]用一个二进制数,每读一个,将原来的a[i]左移一位,并加上当前的一位。

显然,对于一行n列的土地,这一行的状态一共有(1<<n)种(比如000,001,010,011,100,101,110,111)

那么,从0枚举到(1<<n)-1,就能枚举到每一种情况。

如何判断是否左右相邻?如果一个状态是左右不相邻的,那将它像左(或右)移一位后,一定是交错的,也就是说没有移动前后都为1的数位。比如,

01001

10010

所以,对于每一个状态i,分别判断i&(i<1)和i&(i>>1)是否都为0,是则合法,g[i] = 1.

如何判断上下相邻?

枚举每一行i及状态j。

如果状态j合法,必须有则g[j]==1,且在j中不能包含不可用的土地。

也就是说,j中包含的每一块土地,都包含在这一列的合法的集合a[i]中,即(j&a[i]) == j.

枚举上一行的状态k。

k,j不能存在上下相邻的土地,即(k&j)==0.

将上一行的状态k加入这一行的状态j中,即f[i][j] += f[i-1][k].

最后得到了最后一行每种状态下的方案数,累加即可。

猜你喜欢

转载自www.cnblogs.com/mogeko/p/11552238.html