1. 程式人生 > 實用技巧 >AcWing 327 玉米田

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;
}