POJ 3254-Corn Fields【基础状压DP】

描述

农夫John已经购买组成一个新茂盛矩形牧场中号Ñ(1≤ m≤12; 1≤ ñ ≤12)平方包裹。他想在一些广场上为奶牛种一些美味的玉米。遗憾的是,一些广场是不育的,不能种植。Canny FJ知道奶牛不喜欢彼此吃东西,所以当选择哪个方块种植时,他避免选择相邻的方块; 没有两个选中的正方形共享边缘。他还没有最终决定种植哪个广场。

作为一个思想开明的人,Farmer John想要考虑如何选择种植方块的所有可能选择。他是如此开放,他认为选择没有正方形是一个有效的选择!请帮助Farmer John确定他可以选择种植方块的方式。

输入

第1行:两个以空格分隔的整数:MN
行2 .. M +1:行i +1描述牧场的第i行,其中N个以空格分隔的整数表示一个正方形是否为肥沃(1为可育,0为不育)

输出

第1行:一个整数:FJ可以选择模数为100,000,000的方格的数量。

思路:这道题的状态很容易想到,dp[i][s]就是第i行状态为s时的方案数,我们需要考虑两个问题。

第一个是判断当前状态是否合法,就是当前行的牛是否被允许这样放置,我是将第i行压做一个状态S,然后对于现在要判断的状态P,如果S|P > S, 说明地图中0的位置放置了牛,说明不合法。

第二个是判断从上一个状态转移过来要保证之前的状态与现在的不冲突,所以对于两个状态S和P,只要S & P == 0就说明不会有任何两头牛上下相邻。边界是dp[0][0] = 1, 目标状态sum(dp[n][s])(s是合法状态)

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
#define ls rt << 1
#define rs rt << 1|1
#define mid ((l + r) >> 1)
#define lson l, mid, ls
#define rson mid + 1, r, rs
const int maxn = 14;
const int mod = 1e9;
ll dp[maxn][1 << maxn], val[maxn];
int mp[maxn][maxn];
bool vis[maxn];

int main()
{
    int n, m;
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; ++i)
    {
        for(int j = 1; j <= m; ++j)
            scanf("%d", &mp[i][j]);
        ll x = 1;
        for(int j = m; j >= 1; --j) //将一行压成一个数代表状态
        {
            if(mp[i][j])
                val[i] += x;
            x *= 2;
        }
    }
    for(int i = 0; i < (1 << m); ++i) //预处理保证不会有牛在同一行相邻
    {
        bool flag = true;
        for(int j = 0; j < m - 1; ++j)
        {
            if((i & (1 << j)) && (i & (1 << (j + 1))))
            {
                flag = false;
                break;
            }
        }
        vis[i] = flag;
    }
    dp[0][0] = 1;
    for(int i = 1; i <= n; ++i)
    {
        for(int s = 0; s < (1 << m); ++s)
        {
            for(int p = 0; p < (1 << m); ++p)
            {
                if(vis[s] && vis[p] && (s & p) == 0 && (val[i] | s) <= val[i] && (val[i - 1] | p) <= val[i - 1])
                    dp[i][s] = (dp[i - 1][p] + dp[i][s]) % mod;
            }
        }
    }
    ll ans = 0;
    for(int i = 0; i < (1 << m); ++i)
        if(vis[i] && (val[n] | i) <= val[n])
            ans = (ans + dp[n][i]) % mod;
    printf("%lld\n", ans);
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值