POJ3254--Corn Fields

题目大意:有一个m*n的场地,1表示可以种草,0表示不可以。在草地上可以放牛,但是,两头牛不能相邻,问有多少种放牛方法(牛的数量不限,不放牛也算一种)。

 

分析:状压DP。状态:dp[i][j]表示第i行第j种状态的方法数。

状态转移方程,慢慢分析。

首先,我们判断一行有多少种放牛方法,因为1不能相邻,且最多只有12列,所以总数不会超过600。接着,判断哪些方法与第一行场地不矛盾,由于只要存在一个位置矛盾,那么整行就无效,所以,我们在存储场地时,反过来存,然后,用按位与操作,就可以接着这个存在性问题了。初始化的问题都解决了。下面就进入状态转移的过程。

从第二行开始枚举,同样判断哪些方法与第二行场地不矛盾,然后判断哪些状态与前一行不矛盾,且与这一行也不矛盾。那么这样子的状态就是可行的。dp[i][j]=dp[i][j]+dp[i-1][k]。


代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

#define MOD 100000000

int dp[15][600];
int state[600];
int g[15];
int m, n;
int tot;

bool ok(int x) {
    if(x & x<<1) return false;
    else return true;
}

void init() {
    tot = 0;
    for(int i = 0; i < 1<<n; i++)
        if(ok(i)) state[tot++] = i;
}

int main() {
    while(~scanf("%d%d", &m, &n)) {
        memset(dp, 0, sizeof(dp));
        memset(state, 0, sizeof(state));
        memset(g, 0, sizeof(g));
        init();
        for(int i = 1; i <= m; i++)
            for(int j = 1; j <= n; j++) {
                int c;
                scanf("%d", &c);
                if(c == 0)
                    g[i] += 1<<(n-j);
            }
        for(int i = 0; i < tot; i++) {
            if(!(state[i] & g[1]))
                dp[1][i]++;
        }
        for(int i = 1; i <= m; i++) {
            for(int j = 0; j < tot; j++) {
                if(!(state[j] & g[i])) {
                    for(int k = 0; k < tot; k++) {
                        if(!(state[k] & g[i-1]) && !(state[j] & state[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[m][i])%MOD;
        printf("%d\n", ans);
    }
    return 0;
}


你可能感兴趣的:(POJ3254--Corn Fields)