BZOJ 1725: [Usaco2006 Nov]Corn Fields牧場的安排 (狀壓DP)
Description
Farmer John新買了一塊長方形的牧場,這塊牧場被劃分成M列N行(1<=M<=12; 1<=N<=12),每一格都是一塊正方形的土地。FJ打算在牧場上的某幾格土地裡種上美味的草,供他的奶牛們享用。遺憾的是,有些土地相當的貧瘠,不能用來放牧。並且,奶牛們喜歡獨佔一塊草地的感覺,於是FJ不會選擇兩塊相鄰的土地,也就是說,沒有哪兩塊草地有公共邊。當然,FJ還沒有決定在哪些土地上種草。 作為一個好奇的農場主,FJ想知道,如果不考慮草地的總塊數,那麼,一共有多少種種植方案可供他選擇。當然,把新的牧場荒廢,不在任何土地上種草,也算一種方案。請你幫FJ算一下這個總方案數。
Input
* 第1行: 兩個正整數M和N,用空格隔開
* 第2..M+1行: 每行包含N個用空格隔開的整數,描述了每塊土地的狀態。輸入的第i+1行描述了第i行的土地。所有整數均為0或1,是1的話,表示這塊土地足夠肥沃,0則表示這塊地上不適合種草
Output
* 第1行: 輸出一個整數,即牧場分配總方案數除以100,000,000的餘數
Sample Input
2 3
1 1 1
0 1 0
Sample Output
9
輸出說明:
按下圖把各塊土地編號:
1 2 3
4
只開闢一塊草地的話,有4種方案:選1、2、3、4中的任一塊。開闢兩塊草地的話,有3種方案:13、14以及34。選三塊草地只有一種方案:134。再加把牧場荒廢的那一種,總方案數為4+3+1+1=9種。
思路:
dp[i][j] 代表前 i 行,j 這種狀態一共有多少种放法。
所以
f[i][j] = f[i][j] + f[i-1][state(0...all)];
首先要判斷 state(i) 的狀態是不是可行的。
mp[i] 代表的是 第 i 行的可以放牧的狀態。
i & (i >> 1) 判斷i 這個狀態是不是有相鄰的情況。
#include<bits/stdc++.h> using namespace std; const int N = 1e8; int f[13][5000],n,m,t,mp[13]; void dp(){ int ans = 0; int all = (1 << m) - 1; for (int i = 0; i <= all; i++) if (((i&(i >> 1)) == 0) && ((i | mp[1]) == mp[1])) f[1][i] = 1; //判斷第一行的狀態。 for (int i = 2; i <= n; i++){ for (int j = 0; j <= all; j++) if (f[i-1][j]){ //前一行,j 這個狀態 可行。 for (int k = 0; k <= all; k++) if ((k & (k>>1)) == 0 && (k & j)==0 && (k | mp[i]) == mp[i]) // 找到當前行 k 這個狀態是都可行。如果可行,加上去。 f[i][k] = (f[i][k] + f[i-1][j]) % N; } } for (int i = 0; i <= all; i++) ans = (ans + f[n][i]) % N; //最後累加答案。 printf("%d\n",ans); return ; } int main(){ scanf("%d%d",&n,&m); for (int i = 1; i <= n; i++){ for (int j = 1; j <= m; j++){ scanf("%d",&t); mp[i] <<= 1; mp[i] += t; } } dp(); return 0; }