1. 程式人生 > >BZOJ 1725: [Usaco2006 Nov]Corn Fields牧場的安排 (狀壓DP)

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