AcWing 327. 玉米田(状态压缩DP)

一、问题

在这里插入图片描述

二、分析

1、思路

这道题与之前所讲解的AcWing 1064. 小国王(状态压缩DP)非常相似,所以如果大家没有思路的话,可以去看看之前的这篇文章,然后再回来做这道题。

这道题的思路也是一样,一行一行的看。至于这种思路为什么是正确的,作者在AcWing 1064. 小国王(状态压缩DP)里面进行过详细地讲解,这里不做过多的介绍了。

i i i行能种植玉米的方式取决于第 i − 1 i-1 i1行。我们可以利用这个方式来书写转移方程。

这一题的变化在于题目中对土地也有了限制,图中写着1的是能种植,写着0的是不能种植。

我们先来分析一下合法状态需要满足的条件:

我们先不考虑土地的限制。
在这里插入图片描述

现在我们考虑一下土地,土地限制的是本行。按照题目所说1是能种植,0是不能种植。但是这样不太好进行后续的判断,现在我们每一行的土地所代表的01进行取反。这样的话,原来是1的土地现在是0,原来是0的土地现在是1。所以在原来是1的地方种植,即在取反后为0的地方种植。这样的话,就会呈现出下面的现象:对于取反后的土地,0能种植,1不能种植。这样的话,就必定满足我们的种植方案和本行的土地情况做&运算得0。

因此,可以将合法状态总结为下面的方式:
在这里插入图片描述

2、状态表示

f [ i ] [ s ] f[i][s] f[i][s]表示在前i行里面种植玉米,并且第i行的状态是s时,所有的种植方案。

3、状态转移

f [ i ] [ s ] = ∑ f [ i − 1 ] [ s s ] f[i][s]=\sum f[i-1][ss] f[i][s]=f[i1][ss]

4、循环设计

最外层肯定是循环i,第二层就循环我们的状态ss,然后再去枚举第i-1层的可能合法状态进行判断。
这样是3层循环,那么最大的计算次数是: 12 ∗ 2 1 2 ∗ 2 1 2 12*2^12*2^12 12212212。由于我们会对状态进行预处理,仅保留二进制表示下没有相邻个1的状态,所以运算次数肯定比这个小。就算是这个运算次数,也没有到达上限,所以我们的方法是不超时的。

5、初末状态

f[0][0]=1即可。即再前0行种植,且种了0个的方案数是1。

最后的状态是f[n+1][0]。

即再前n+1行中种玉米,但是第n+1行不种玉米的方案数。

三、代码

#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
const int N=15,S=1<<12,mod=1e8;
long long g[N],f[N][S];
vector<int>state;
int n,m;
bool check(int x)
{
    for(int i=0;i+1<m;i++)
    {
        if((x>>i&1)&&(x>>(i+1)&1))return false;
    }
    return true;
}
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<m;j++)
        {
            int x;
            cin>>x;
            g[i]+=(!x)*1<<j;
        }
    }
    for(int i=0;i<1<<m;i++)
    {
        if(check(i))state.push_back(i);
    }
    f[0][0]=1;
    for(int i=1;i<=n+1;i++)
    {
        for(int s=0;s<state.size();s++)
        {
            if(g[i]&state[s])continue;
            for(int ss=0;ss<state.size();ss++)
            {
                if(g[i-1]&state[ss])continue;
                if((state[s]&state[ss])==0)
                    f[i][state[s]]=(f[i][state[s]]+f[i-1][state[ss]])%mod;
            }
        }
    }
    cout<<f[n+1][0]<<endl;
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值