POJ3254(状压DP)

题意

给你一个n*m的矩阵(农田),1表示可以种植,0表示不能种植。且上下和左右每个单元不能相邻。问有多少种种植方法(可以不种植)。

样例

Sample Input

2 3
1 1 1
0 1 0

Sample Output

9

1 2 3

0 4 0

种植一棵树(1,2,3,4)4种,两棵(13,14,34)3种,三棵(134)1种,零棵树1种,sum = 4+3+1+1 = 9。

题解

第一行可行状态有

1 0 0,0 1 0,0 0 1,1 0 1,0 0 0 。 5种

第二行可行状态是

0 0 0(1状态),0 1 0(2状态)

1状态下可行下第一行可行状态有全5种,2状态可行下有(1 0 0, 0 0 1,1 0 1,0 0 0)4种。总共9种。

所以dp的转移方程式

dp[i][state] +=dp[i-1][pre_state] = dp[i-1][pre_state(k1)]+dp[i-1][pre_state(k2)]+......+dp[i-1][pre_state(km)]

即此行状态可行时,上一行满足的状态总和就是此状态的可行情况。那么此行的可行状态就是此行所有可行状态总和。

所以ans = dp[n][state(j1)]+dp[n][state(j2)]+...+dp[n][state(jm)]。

代码

#include<cstdio>
#include<algorithm>
#include<string.h>
#include <string.h>
using namespace std;

typedef long long ll;
int dp[15][1<<15];
bool is[15][15]; //记录哪一处可种植
int n, m;
const int Mod = 1e8;
bool check(int x, int state)
{
    if(state & (state<<1)) return false;
    for(int i = 1; i <= m; i++)
    {
        if(!is[x][i]) //若这处是不能种植的
        {
            if( ((1<<(m-i)) & state) != 0) //但是这种状态下此处种了树
                return false;
        }
    }
    return true;
}
int main()
{
    while(scanf("%d%d", &n, &m) != EOF)
    {
        //memset(dp, 0, sizeof(dp));
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= m; j++)
                scanf("%d", &is[i][j]);
        dp[0][0] = 1;
        ll ans = 0;
        for(int i = 1; i <= n; i++)
        {
            for(int j = 0; j < (1<<m); j++)
            {
                if(check(i,j)) {
                    for(int k = 0; k < (1<<m); k++)
                        if(!(k&j)) dp[i][j] += dp[i-1][k];
                }
                if(i == n) ans = (ans+dp[i][j]) % Mod;
            }
        }
        printf("%I64d", ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/QiHang_QiHang/article/details/81258794
今日推荐