USACO Corn Fields

洛谷 P1879 [USACO06NOV]玉米田Corn Fields

洛谷傳送門數組

題目描述

Farmer John has purchased a lush new rectangular pasture composed of M by N (1 ≤ M ≤ 12; 1 ≤ N ≤ 12) square parcels. He wants to grow some yummy corn for the cows on a number of squares. Regrettably, some of the squares are infertile and can't be planted. Canny FJ knows that the cows dislike eating close to each other, so when choosing which squares to plant, he avoids choosing squares that are adjacent; no two chosen squares share an edge. He has not yet made the final choice as to which squares to plant.ide

Being a very open-minded man, Farmer John wants to consider all possible options for how to choose the squares for planting. He is so open-minded that he considers choosing no squares as a valid option! Please help Farmer John determine the number of ways he can choose the squares to plant.spa

農場主John新買了一塊長方形的新牧場,這塊牧場被劃分紅M行N列(1 ≤ M ≤ 12; 1 ≤ N ≤ 12),每一格都是一塊正方形的土地。John打算在牧場上的某幾格裏種上美味的草,供他的奶牛們享用。code

遺憾的是,有些土地至關貧瘠,不能用來種草。而且,奶牛們喜歡獨佔一塊草地的感受,因而John不會選擇兩塊相鄰的土地,也就是說,沒有哪兩塊草地有公共邊。get

John想知道,若是不考慮草地的總塊數,那麼,一共有多少種種植方案可供他選擇?(固然,把新牧場徹底荒廢也是一種方案)it

輸入格式

第一行:兩個整數M和N,用空格隔開。io

第2到第M+1行:每行包含N個用空格隔開的整數,描述了每塊土地的狀態。第i+1行描述了第i行的土地,全部整數均爲0或1,是1的話,表示這塊土地足夠肥沃,0則表示這塊土地不適合種草。ast

輸出格式

一個整數,即牧場分配總方案數除以100,000,000的餘數。class

輸入輸出樣例

輸入 #1複製angular

輸出 #1複製

題解:

看到這個數據範圍,和這個招牌的01串。應該能想到是狀壓\(DP\)計數的問題。

(話說狀壓\(DP\)就是按數據範圍碰?)

那麼咱們考慮把狀態設置成:\(dp[i][j]\)表示第\(i\)行狀態爲\(j\)的時候的方案數。

如今這道題最讓咱們無所適從的條件就是判斷這塊地能不能種草。

咱們容易發現:不能種草的狀況只有兩種:有一些地原本就是荒蕪的,不能種草。另外有一些地是由於相鄰的地被種上草了因此不能種草。因此咱們在轉移的時候必定要把判斷條件處理好了。

這個怎麼去判斷呢?

須要高深的位運算知識

  • 首先咱們開一個數組來存初始狀態,即輸入時的狀態(即\(F[i]\)數組),之因此要存儲這個初始狀態是爲了處理原本就荒蕪的狀況。那麼,對於咱們當前枚舉到的狀態\(j\),須要把它和初始狀態進行對比。初始狀態能夠種草的地能夠種也能夠不種,可是初始狀態不能夠種草的地絕對不能種。要判斷當前狀態\(j\)合法,只須要把當前狀態和\(F[i]\)進行對比,若是不讓種的地都沒種,那麼無論能夠種的地到底種沒種,這個狀態都是合法的。

因此咱們的判斷條件就是\(j\&F[i]==j\)。這樣的話,若是\(j\)在不應種草的地方種上了草(得1),那麼它與上0會得0,就不等於\(j\)了。

  • 而後咱們考慮相鄰格子,相鄰格子有兩種:橫向相鄰和縱向相鄰

顯然,若是一個狀態合法,在橫向上會有\(010101...\)這樣的狀況出現,也就是說對於一個1,它的前一位和後一位確定是\(0\).那麼咱們開一個狀態數組\(st[i]\)。若是它符合「\(010101...\)」的條件,那麼顯然會有:

\(i\&(i<<1)=0,i\&(i>>1)=0\)

那麼縱向相鄰怎麼判呢?能夠發現,縱向是否相鄰是由當前枚舉到的狀態決定的。因此沒法預處理,只能在枚舉當前狀態時看。

當咱們枚舉到一個狀態:\(dp[i][j]\)的時候,咱們須要知道的是\(dp[i-1]\)的狀態。不須要考慮\(dp[i+1]\)的狀態的緣由是動態規劃的無後效性。那麼咱們只須要再枚舉\(dp[i-1]\)的狀態,依次確認是否合法便可。合法的條件就是\(k\&j=0\)。也就是說,在\(k\)能種的地上\(j\)必須選擇不種。

那麼轉移方程就是:
\[ dp[i][j]=dp[i][j]+dp[i-1][k]\quad(mod\,\,p) \]
最終的答案還要統計全部的方案數。須要把全部狀態枚舉一遍,依次累加\(dp[m][i]\)

那麼這道題就完事了。難點不是狀壓的過程,是狀壓合法轉移的判斷過程。

代碼:

#include<cstdio>
#include<bitset>
using namespace std;
const int mod=1e9;
int m,n;
int map[20][20];
int dp[20][1<<12],F[20];
bool st[1<<12];
//dp[i][j]表示第i行狀態爲j的時候的方案數
int main()
{
    scanf("%d%d",&m,&n);
    for(int i=1;i<=m;i++)
        for(int j=1;j<=n;j++)
        {
            scanf("%d",&map[i][j]);
            F[i]=(F[i]<<1)+map[i][j];
        }
    for(int i=0;i<(1<<n);i++)
        st[i]=((i&(i<<1))==0) && (((i&(i>>1))==0));
    dp[0][0]=1;
    for(int i=1;i<=m;i++)
        for(int j=0;j<(1<<n);j++)
            if(st[j] && ((j&F[i])==j))
                for(int k=0;k<(1<<n);k++)
                    if((k&j)==0)
                        dp[i][j]=(dp[i][j]+dp[i-1][k])%mod;
    int ans=0;
    for(int i=0;i<(1<<n);i++)
        ans=(ans+dp[m][i])%mod;
    printf("%d",ans);
    return 0;
}
相關文章
相關標籤/搜索