poj3254 狀壓dp

題目鏈接:點擊打開鏈接


題意:

給一個 n * m 的矩陣;

其中 0 表示可以在此處種玉米;

1 則相反;

而農夫規定兩株玉米不能種在一起;

即一株玉米的上下左右都沒有其它玉米;

問有多少種種法;

其中不種也是一種方法;


理解:

狀態壓縮 dp;

參考:點擊打開鏈接 和 點擊打開鏈接

遞推式含義:dp[i][j] 表示在第 i 行的 j 狀態有多少種方法;

遞推式:dp[i][j] += dp[i - 1][k];

其中 j 要滿足條件(0)(1),k 也要滿足條件(0)(1)(2);

條件(0):

j 狀態裏的 1 的位置不相鄰;

即:j & (1 << j) != 0;

條件(1):

原矩陣中的 0 的位置在 j 狀態中不能爲 1 ;

即:(w[i] & j) ==  0;

其中 w[i] 表示第 i 行中數值爲 0 的集合的一個整數,由二進制轉化爲十進制;

條件(2):

第 i - 1 行的 k 狀態不能與第 i 行的 j 狀態衝突;

即:v[j] & v[k] == 0;

其中 v[i] 表示符合條件(1)的 i 狀態;


代碼如下:


#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <ctime>

#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
#include <map>
#include <set>
#include <queue>
#include <stack>

using namespace std;

typedef long long LL;
typedef pair<int, int> PII;

const int MIN_INF = 1e-7;
const int MAX_INF = (1e9) + 7;

#define X first
#define Y second

LL dp[13][1 << 13];
int v[1 << 13], w[13];

const int MOD = 100000000;

int main() {
    int n, m;
    while (cin >> n >> m) {
        memset(dp, 0, sizeof(dp));
        memset(v, 0, sizeof(v));
        memset(w, 0, sizeof(w));
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < m; ++j) {
                int x;
                cin >> x;
                w[i] += (x == 0 ? (1 << j) : 0); // w[i] 存儲的是 0 的位置,並將其轉化爲整數;
            }                                    // 其中狀態與其 & 則可知該狀態是否全在 1 位;
        }                                        // 若 & 後爲 0 則全在 1 位;
        // 若爲 1 則存在 1 在 0 位的狀態,則不符合題意;
        int k = 0;
        for (int i = 0; i < (1 << m); ++i) { //將所有狀態轉化爲數字,並去掉相鄰位都爲 1 的狀態;
            if ((i & (i << 1)) == 0) {
                v[k++] = i;
            }
        }

        for (int i = 0; i < k; ++i) { // dp 初始值
            if ((w[0] & v[i]) == 0) { // 判斷該狀態是否在 i 行可行;
                dp[0][i] = 1;         // 若爲 1 則該狀態沒有全在 1 位,不符合題意;
            }
        }

        for (int i = 1; i < n; ++i) {
            for (int j = 0; j < k; ++j) {
                if (w[i] & v[j]) { // 判斷該狀態是否在 i 行可行;
                    continue;
                }
                for (int l = 0; l < k; ++l) {
                    if (w[i - 1] & v[l])  { // 判斷該狀態是否在 i - 1 行可行;
                        continue;
                    }
                    if ((v[j] & v[l]) == 0) { //判斷該狀態與上一個狀態是否衝突;
                        dp[i][j] = (dp[i][j] + dp[i - 1][l]) % MOD; //加了再 MOD;
                    }
                }
            }
        }

        LL ans = 0;
        for (int i = 0; i < k; ++i) {
            ans = (ans + dp[n - 1][i]) % MOD;
        }

        cout << ans % MOD << endl;
    }

    return 0;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章