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;
}

 

發佈了84 篇原創文章 · 獲贊 9 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章