【poj 3254】Corn Fields 状压动规

传送门

【题目大意】一个矩阵里有很多格子,每个格子有两种状态,可以放牧和不可以放牧,可以放牧用1表示,否则用0表示,在这块牧场放牛,要求两个相邻的方格不能同时放牛,即牛与牛不能相邻。问有多少种放牛方案(一头牛都不放也是一种方案)

额,好像是玉米。。

举个例子:

2 3
1 1 1
0 1 0

表示2*3的玉米地,现在一共有多少种种植方法呢? 答案:种0个玉米(算一个合法方案)+种1个玉米(4)+种2个玉米(3)+种3个玉米(1)=9
题解:
PS:位运算的优先级相当的低,一般情况下多打几个()来提高优先级终归是好习惯。

这个题目,考虑,将每个点是否种植玉米若种就在相应的二进制表示。

然后每一行的种植方案就和前一行有关。所以当当前状态合理,前一排的状态合理,且两行没有在相同位置为1。似乎作为动规来说这个题目难度不大,主要难度在用二进制表示每个状态和判断每一个状态是否合理。

1:判断这个状态是否能种
S【i】是表示的每一个不能种玉米的地方;
若state & s【i】 != 0则在不能种玉米的地方种植了玉米。

2inline bool check_T(int y,int state){
    bool c = false;int h = 1;
    for(;h <= state;h <<= 1)
    {
        if(state & h && c == true) return false;
        c = false;
        if(state & h) c = true;
    }
    return true;
}
这个函数就是枚举的对于一种种植方案是否有同一行相邻位置种植了玉米,也就是对应的是否有在状态的二进制表示中有连续两个13:(j & k) == 0 这个就是我的两行中相邻位置有没有都种植玉米,所以相对应的情况下就是两个状态i和状态j的二进制表示中有没有同一个位子都为1

代码

#include <iostream>
#include <cstdio>

const int MOD = 100000000;

using namespace std;

int N,M,Max,ans,dp[20][10000],S[15];
bool Map;

inline bool check(int y,int state){     //判断状态是否可行
    if(S[y] & state) return false;
    return true;
}

inline bool check_T(int y,int state){
    bool c = false;int h = 1;
    for(;h <= state;h <<= 1)
    {
        if(state & h && c == true) return false;
        c = false;
        if(state & h) c = true;
    }
    return true;
}

int main()
{
    scanf("%d %d",&M,&N);

    for(int i = 0;i < M;i++)
        for(int j = 1;j <= N;j++){
            scanf("%d",&Map);
            if(Map == 0) S[j] += 1 << i;
        }

    Max = (1 << M) - 1;

    for(int i = 0;i <= Max;i++)     //循环边界
        if(check(1,i) && check_T(1,i)) dp[1][i] = 1;

    for(int i = 2;i <= N;i++)
        for(int j = 0;j <= Max;j++)
        {
            if(check(i,j) && check_T(i,j)){
                for(int k = 0;k <= Max;k++)
                if((j & k) == 0 && check(i-1,k) && check_T(i-1,k))
                    dp[i][j] += dp[i-1][k];
            }
        }

    for(int i = 0;i <= Max;i++)
        ans += dp[N][i],
            ans %= MOD;

    cout<<ans;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值