洛谷傳送門數組
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\)的時候的方案數。
如今這道題最讓咱們無所適從的條件就是判斷這塊地能不能種草。
咱們容易發現:不能種草的狀況只有兩種:有一些地原本就是荒蕪的,不能種草。另外有一些地是由於相鄰的地被種上草了因此不能種草。因此咱們在轉移的時候必定要把判斷條件處理好了。
這個怎麼去判斷呢?
須要高深的位運算知識
因此咱們的判斷條件就是\(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; }