1. 程式人生 > >POJ 3254 Corn Fields(狀態壓縮DP入門)

POJ 3254 Corn Fields(狀態壓縮DP入門)

lang gre eas lse tile not using which 有一個

Description

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.

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.

Input

Line 1: Two space-separated integers: M and N
Lines 2..M+1: Line i+1 describes row i of the pasture with N space-separated integers indicating whether a square is fertile (1 for fertile, 0 for infertile)

Output

Line 1: One integer: the number of ways that FJ can choose the squares modulo 100,000,000.

Sample Input

2 3
1 1 1
0 1 0

Sample Output

9

思路:題目大意是農夫有一塊地,被劃分成一個n*m的放個區域,每個區域有一個數字0或1,1代表可以放牛,0代表不可以. 每一個位置可以放牛的條件是上下左右相鄰位置都沒有放牛;現在給定一個n*m的序列問你農夫放牛的情況有多少種?
解題:將每一行的01序列(也就是放牛的狀態)看成是二進制數將之轉換成十進制數存起來,我們就將這個二維的問題轉換成了一位,每一次只需要對每一行進行判斷即可。
以樣例第一行為例子,有0,0,0; 0,1,0; 1,0,0; 0,0,1; 1,0,1;這五種可能放牛的情況,我們可以將這每一行的01序列成一個二進制序列將之轉化成一個10進制數就為0,2,1,4,5;這樣一個十進制數就可以表示每一行可以放牧的狀態;
   而第二行則有0,0,0; 0,1,0;而這一行的每一種情況還要考慮跟上一行有沒有相鄰的情況,0,0,0與上一行的五種都無相鄰,而0,1,0與上一行0,1,0相鄰所以只有四種 所以答案是5+4 = 9;
emmm。。。為也是初學 可能很多地方講的比較模糊,望見諒

 1 #include<iostream>
 2 #include<algorithm>
 3 #include<cstring>
 4 
 5 using namespace std;
 6 const int mod = 1e9;
 7 const int N = 13;
 8 int pass[1 << N], now[1 << N];//存儲可以每一列可以放牧的種類數,mp代表每一行的不可以放牧的狀態
 9 int dp[N][1 << N];//dp[i][j] 代表第i行按第j種放牧情況的種類數
10 int n, m;
11 bool check(int x) {
12     return (x&(x << 1));//判斷一行是否有相鄰放牧的情況
13 }
14 bool judge(int x, int y) {
15     return (now[x] & pass[y]);//判斷當前第x行不可以放牧狀態x和可以放牧狀態y的交集
16                            //也就是如果在不可以放牧的位置上有放牧的這一可能就返回真(即不可以放牧)否則這一種放牧情況y就可以
17 }
18 int main()
19 {
20     ios::sync_with_stdio(false);
21     while (cin >> n >> m) {
22         memset(pass, 0, sizeof(pass));
23         memset(now, 0, sizeof(now));
24         memset(dp, 0, sizeof(dp));
25         for (int i = 1; i <= n; i++)
26             for (int x,j = 1; j <= m; j++) {
27                 cin >> x;
28                 if (x == 0)
29                     now[i] += (1 << (j - 1));//將不可放牧的狀態存入數組,將每一行01序列轉換成10進制數
30             }
31         int cnt = 0;
32         for (int i = 0; i < (1 << m); i++)//因為有m列所以每一行的狀態有2^m種 0為不放牧的情況
33             if (!check(i))//每一種狀態都不存在相鄰的放牧位置的時候即為合法狀態
34                 pass[cnt++] = i;
35         for (int i = 0; i < cnt; i++)
36             if (!judge(1, i))//如果當前的合法狀態跟第一行的不可放牧狀態沒有交集的時候就代表可以放牧 
37                 dp[1][i] = 1;//因為是第一行所以種類數為1
38         for (int i = 2; i <= n; i++) {
39             for (int j = 0; j < cnt; j++) {//判斷當前行的放牧狀態
40                 if (judge(i, j))continue;//如果當前第j種狀態不可行
41                 for (int k = 0; k < cnt; k++) {//判斷上一行的放牧狀態
42                     if (judge(i - 1, k))continue;
43                     if (!(pass[j] & pass[k])) {//如果當前行第j種狀態可行並且和上一行第k種狀態不沖突
44                         dp[i][j] += dp[i - 1][k];//就加上上一行第k種狀態的種數
45                         dp[i][j] %= mod;
46                     }
47                 }
48             }
49         }
50         int ans = 0;
51         for (int i = 0; i < cnt; i++)
52             ans = (ans + dp[n][i]) % mod;
53         cout << ans << endl;
54     }
55     return 0;
56 }



POJ 3254 Corn Fields(狀態壓縮DP入門)