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


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值