POJ 3254 Corn Fields(状态dp)

本文介绍了一种利用状态动态规划解决玉米种植问题的方法。问题要求在满足特定约束条件下(如不能相邻种植),计算出在M*N的矩阵中有多少种合法的种植方案。通过状态压缩与位运算技巧,文章详细阐述了如何建立并求解动态规划模型。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

状态dp一般就是用二进制位来表示当前状态,本质还是动态规划,所以要找到转移方程,一般dp需要满足的就是最优子结构、无后效性,状态dp可能加入了一些位运算,加快程序的执行。

题目大意: 给出一个M*N的矩阵,元素为0表示这个地方不能种玉米,为1表示这个地方能种玉米,现在规定所种的玉米不能相邻,即每行或者没列不能有相邻的玉米,问一共有多少种种植方法。

分析: 每一行种植玉米的状态其实之和前一行的种植状态有关系,和它前面的其余行没关系,这个满足无后效性,题目让我们求的是方案总数,其实就是求最后一行的所有状态的方案数加和。
令dp[i][state]表示第i行状态为state的方案数,那么dp[i][state] += dp[i-1][pre_state],这里pre_state和state必须满足条件,1:不相邻 2:pre_state和state是可以种植玉米的,因为有的地不能种植玉米,相应位不能为1,该转移方程可以理解为前一行的每一个pre_state都为第i行达到状态state贡献了dp[i-1][pre_state]个方案数。这个和用1*2覆盖地板的那个方案一样。

有几个位运算的技巧:
1.不考虑地是否可以种植,先就状态上考虑合法的种植方法,位运算如下:

void getAllStates() {//get all the situation 1 not close 1 
    for (int i = 0; i < (1 << col); ++i) {
        if (!(i&(i << 1))) {
            st[state_num++] = i;
        }
    }
}

2.考虑当前行土地的情况,看一个状态是否合法,位运算如下:

bool isValid(int state, int index) {
    int rstate = 0;
    for (int i = 1; i <= col; ++i) {
        if (pasture[index][i]) {
            rstate += (1 <<  (col - i));
        }
    }
    if (!(state&(~rstate))) {
        return true;
    }
    return false;
}

3.两个状态是否合法,即不能相邻,位运算如下:

 !(curstate&prestate)

代码如下:

#include<iostream>

const int Maxstate = 4100;
const int MaxN = 13;

int pasture[MaxN][MaxN];
int st[Maxstate];
int dp[MaxN][Maxstate];
int state_num, row, col;

void getAllStates() {//get all the situation 1 not close 1 
    for (int i = 0; i < (1 << col); ++i) {
        if (!(i&(i << 1))) {
            st[state_num++] = i;
        }
    }
}
bool isValid(int state, int index) {
    int rstate = 0;
    for (int i = 1; i <= col; ++i) {
        if (pasture[index][i]) {
            rstate += (1 <<  (col - i));
        }
    }
    if (!(state&(~rstate))) {
        return true;
    }
    return false;
}
int main() {
    int m, n;
    std::cin >> m >> n;
        for (int i = 1; i <= m; ++i) {
            for (int j = 1; j <= n; ++j) {
                std::cin >> pasture[i][j];
            }
        }
        row = m;
        col = n;
        state_num = 0;

        getAllStates();
        memset(dp, 0, sizeof(dp));
        dp[0][0] = 1;
        for (int i = 1; i <= m; ++i) {
            for (int j = 0; j < state_num; ++j) {
                if (isValid(st[j], i)) {//当前种植状态st[j]是否可以种在第i行土地上
                    for (int pre = 0; pre < state_num; ++pre) {
                        if (isValid(st[pre], i - 1) && !(st[j] & st[pre])) {
                            dp[i][st[j]] += dp[i - 1][st[pre]];
                        }
                    }
                }
            }
        }
        int ans = 0;
        for (int i = 0; i < state_num; ++i) {
            ans = (ans + dp[row][st[i]]) % 100000000;
        }
        std::cout << ans << std::endl;


}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值