花園主John新買了一塊長方形的花園,這塊花園被精心地劃分成M行N列(1≤M≤12,1≤N≤12),每一格都是一塊正方形的花壇。John計劃在花園的某些花壇裡種上鮮豔的花朵,讓他的蜜蜂們能夠採集花蜜。然而,有些花壇的土壤質量不佳,不適合種植花朵。另外,為了保持花園的美觀和蜜蜂的舒適度,John不會選擇相鄰的花壇種植花朵,也就是說,沒有哪兩個花壇是相鄰的(即它們沒有公共邊)。John想知道,如果不考慮花朵的總數,那麼,他有多少種不同的種植方案可以選擇?(當然,將花園完全保持原樣不種植花朵也是一種方案)
輸入格式
第一行:兩個整數M和N,用空格隔開,分別代表花園的行數和列數。
第2到第M+1行:每行包含N個用空格隔開的整數,描述了每個花壇的土壤狀態。第i+1行描述了第i行的花壇狀態,所有整數均為0或1,其中1表示花壇土壤肥沃適合種植花朵,0則表示土壤不適合種植。
1<=M<=12,1<=N<=12
輸出格式
一個整數,即牧場分配總方案數除以100000000的餘數。
輸入/輸出例子1
輸入:
2 3
1 1 1
0 1 0
輸出:
9
樣例解釋
無
注意到範圍很小,且和dp可能扯上關係
考慮狀壓。
但是題目給了一些限制條件
1.對於同一行,相鄰列不同時為1,開個迴圈判斷即可,假設本行選了狀態是s, (s<<i) & (s<<(i+1)) ==0,s的第 i 位和第 i+1 位不同時為1即可。
2.對於不同行,相同列,相鄰行不同時為1,這個更簡單了,假設第一行選了狀態是x,第二行選了狀態是y,x & y == 0
然後我們可以預處理每一行的合法狀態,裝入vec,這樣可以減少列舉量,也方便一些
狀態和轉移就顯而易見了。
f(i, s) 當前考慮前i行,並且第i行的狀態是s的方案數
假設s2是第i行選的狀態,s1是第i-1行選的狀態。滿足限制條件時(只有限制2,因為這是對於不同行時的轉移),f(i, s2) += f(i-1, s1)
如何預處理合法狀態?這裡給一個小知識點
判斷一個集合是否是另外一個集合的子集
s2 & s == s2
s2是s的子集
所以我們先處理每一行合法狀態的最大值(就是每一行,可以種植的地,對應的二進位制和)
如何列舉二進位制狀態,前提是每個二進位制狀態是合法狀態最大值的子集才行,如何因為這是同一行,就只有限制1了。
#include <bits/stdc++.h> #define int long long using namespace std; const int N=15, M=8500, Mod=100000000; int n, m, a[N][N], f[N][M], ans=0; vector<int> v[N]; bool check(int x) { for (int i=0; i<=12; i++) if (((x<<i) & (x<<(i+1)))!=0) return 0; return 1; } signed main() { scanf("%lld%lld", &n, &m); for (int i=1; i<=n; i++) for (int j=1; j<=m; j++) scanf("%lld", &a[i][j]); for (int i=1; i<=n; i++) { int s=0; for (int j=1; j<=m; j++) if (a[i][j]) s+=(1<<(j-1)); for (int j=0; j<(1<<m); j++) if ((j&s)==j && check(j)) v[i].push_back(j); } for (int i=0; i<v[1].size(); i++) f[1][v[1][i]]=1; for (int i=2; i<=n; i++) for (int j=0; j<v[i].size(); j++) for (int k=0; k<v[i-1].size(); k++) if ((v[i][j]&v[i-1][k])==0) f[i][v[i][j]]=(f[i][v[i][j]]+f[i-1][v[i-1][k]])%Mod; for (int i=0; i<v[n].size(); i++) ans=(ans+f[n][v[n][i]])%Mod; printf("%lld", ans); return 0; }