AcWing 327 玉米田

AcWing 327 玉米田

题目描述

农夫约翰的土地由 \(M \times N\) 个小方格组成,现在他要在土地里种植玉米。

非常遗憾,部分土地是不育的,无法种植。

而且,相邻的土地不能同时种植玉米,也就是说种植玉米的所有方格之间都不会有公共边缘。

(注意:这里是上下左右边缘,不是两斜对角边缘)

现在给定土地的大小,请你求出共有多少种种植方法。

土地上什么都不种也算一种方法。

输入格式

\(1\) 行包含两个整数 \(M\)\(N\)

\(2 \dots M+1\) 行:每行包含 \(N\) 个整数 \(0\)\(1\),用来描述整个土地的状况,\(1\) 表示该块土地肥沃,\(0\) 表示该块土地不育。

输出格式

输出总种植方法对 \(100000000\) 取模后的值。

数据范围

\(1 \le M,N \le 2\)

输入样例:

2 3
1 1 1
0 1 0

输出样例:

9

算法解析

配合算法进阶课 y 总讲解视频使用更佳。

算法构造

经典的棋盘型状态压缩动态规划,我们可以按照之前 Acwing 上 P1064 小国王的思路,处理本题。

首先,我们需要明确,题目的要求:

统计方案数,有些土地不能种植

状态设计

首先,我们得明确状态是什么。

我们这个状态,肯定是要统计方案数。

我们这个状态,必然需要表示每一行土地种植的状态。

因此得到:

\[f[i][s] 表示已经种植前 i 行,且第 i 行种植的状态为 s 的方案数 \]

状态转移

题目的限制条件,其实就是我们转移的限制条件。

我们知道,这里是十字形的禁止种植,也就是上下左右不能有相邻的两棵玉米。

那么怎么判断呢?

如果说我们把 \(1\) 表示这个地方种植玉米,\(0\) 表示不种植

\[S=1110 \quad 1,2,3 这三个地方种玉米,第四个地方不种植玉米 \]

对于一行而言,不能种植相邻的玉米。

即:

对于一行而言,不能有相邻的 \(1\)

\[S=1110 \quad 是不合法的状态 \]

对于相邻的两行而言,不能在同一列都种植玉米

\[a=1010 \quad b=1000 \]

这是不可以的,在第一个位置会出现上下矛盾

那么我们可以转化为:

\[a \& b==0 \]

最后,对于题目中的土地不能种植,我们可以认为。

\[如果第 i 行的状态为 s,那么荒废土地处不能有 1 \]

我们可以设计一个数组:$$g[i] $$表示第 i 行不能种植土地的状态 $${}\quad g[1]=1011 \quad $$表示第一行,

第一个,第三个,第四个位置不能种植玉米

总而言之

\[第 i 行的状态为 s \quad 那么 s \& g[i]==0 \]

代码解析

#include <bits/stdc++.h>
using namespace std;
const int N=13,Mod=100000000;
vector<int> state,head[1<<N];
int n,m,x,f[14][1<<N],g[N];
inline bool check(int x)//快速判断有没有相邻的1
{
    return !(x&x>>1);
}
inline void init()
{
    scanf("%d%d",&n,&m);
    for(int i=1; i<=n; i++)
        for (int j=1; j<=m; j++)
        {
            scanf("%d",&x);
            g[i]+=(!x<<(j-1));//荒废土地是0,我们在这里转换为1
        }
    for(int i=0; i<(1<<m); i++)
        if (check(i))//这个状态不存在种植左右相邻的玉米
            state.push_back(i);
    for(int i=0; i<state.size(); i++)
        for(int j=0; j<state.size(); j++)
            if (!(state[i] & state[j]))//i对应的状态和j对应的状态没有在同一列种植玉米
                head[i].push_back(j);
    f[0][0]=1;
    for(int i=1; i<=n+1; i++)
        for(int a=0; a<state.size(); a++)
        {
            if (state[a] & g[i])//在第i行,状态a是否满足在荒废土地没有种植玉米
                continue;
            for(int b=0; b<head[a].size(); b++)//从上一行b对应的状态,转到本行a对应的状态
                f[i][a]=(f[i][a]+f[i-1][head[a][b]])%Mod;
        }
    printf("%d\n",f[n+1][0]);//表示第n+1行什么都没种植的状态,其实就是累加f[n][S]
}
signed main()
{
    init();
    return 0;
}
posted @ 2020-08-04 13:54  SweepyZhou  阅读(274)  评论(0编辑  收藏  举报