POJ 3254 Corn Fields (状压DP) 解题报告

传送门:http://poj.org/problem?id=3254

这题算是状压DP的入门题吧。之所以用状压DP来解,是因为这个题可以利用状压DP的优势。状压DP可以利用数字表示一个状态,从而降低问题的空间和时间复杂度,而且也能方便问题的表示和解决。如果不用状压,那么表示一个状态将会有很多维度。

既然是DP问题,那么就要有递推方程啦~这个题的话首先要思考怎么构造数组来进行DP。我们可以用一个二维数组,一维用来表示当前的行标号,一维用来表示这一行要采用什么状态,最后这个内容可以用来表示方法数总和。这样就可以很方便的达到“总结历史”的效果。

具体的还是到代码里讲吧。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int MOD = 100000000;
int row[20], state[1 << 12], dp[20][1 << 12];   // row --》 土地的实际情况
// state ——》所有的符合左右不相邻的状态


int main()
{
   //freopen("input.txt", "r", stdin);
    int m, n;
    while(~scanf("%d %d", &m, &n)){
        int top = 0;
        memset(row, 0 , sizeof(row));
        memset(state, 0, sizeof(state));
        memset(dp, 0, sizeof(dp));
        for(int i = 1;i <= m;++i){ // 读入初始状态
            for(int j = 1;j <= n;++j){
                int temp;
                scanf("%d", &temp);
                if(temp == 0)
                    row[i] += (1 << (n - j)); //这里要注意一下,不可种植的土地用1来表示,方便后面计算。
            }
        }
        for(int i = 0;i < (1 << n);++i){  // 这里的上界是 (1 << n - 1),左移n位正好会比所需位数多一位
            if(!(i & (i << 1)) )
                state[++top] = i;  // 所有左右不相邻状态统计
        }
        for(int j = 1;j <= top;++j){
            if(!(state[j] & row[1]))
                dp[1][j] = 1;
        }
        for(int i = 2;i <= m;++i){
            for(int j = 1;j <= top;++j){
                if(!(state[j] & row[i])){
                    for(int k = 1;k <= top;++k){ // 如果上一行的状态为state[k]
                        if(!(state[j] & state[k])){ //保证第i行的state[j]状态和上一行的state[k]状态不会有冲突
                           if(!(state[k] & row[i - 1])){  //而且还要保证这次所选的上一行的状态state[k]不和上一行的实际情况相冲突
                                dp[i][j] = (dp[i][j] + dp[i - 1][k]) % MOD; //这里用好像是用不了滚动数组。
                           }
                        }
                    }
                }
            }
        }
        int ans = 0;
        for(int i = 1;i <= top;++i){  //这里统计答案的时候,要统计最后一行的每一种状态的方案数。
            ans = (ans + dp[m][i]) % MOD;
        }
        printf("%d\n", ans);

    }
    return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值