看到这个题,又看了看数据规模,立马明白是一道状态压缩dp。
用 f [ i ][ j ] 表示第 i 行的状态为 j 时,前 i 行的方案总数 ( 状态用二进制表示,1表示放奶牛,0表示不放 ) 。
用 ok[ i ] 表示状态为 i 时是否合法,用 m[ i ]来储存第 i 行的肥沃土地。
预处理出每一行的合法状态(即没有两个格子相邻),然后以 每行 为阶段进行转移即可。
具体在转移时,除了考虑之前 ok 数组所记录的状态是否合法,还需要考虑:
1.与上一行是否冲突((j & k) == 0)
2.当前行的奶牛的位置上是否都有草((m[i] | j) == m[i])
那么不难写出代码。
1 #include <bits/stdc++.h>
2 using namespace std;
3 const int mod = 1e8;
4 const int MAXN = 12 + 1;
5
6 inline int read()
7 {
8 static int x;
9 scanf(" %d", &x);
10 return x;
11 }
12
13 int M, N;
14 bool ok[1 << 13];
15 int f[13][1 << 13], m[MAXN];
16
17 int main()
18 {
19 cin>>M>>N;
20
21 for(int i = 1; i <= M; i++)
22 for(int j = 0; j < N; j++)
23 m[i] |= (read() << j);
24
25 bool flag;
26 for(int i = 0; i < (1 << N); i++)
27 {
28 flag = false;
29 for(int j = 1; j < 13; j++)
30 if( ((i >> j) & 1) & ( (i >> (j - 1)) & 1) ) flag = true;
31 ok[i] = (flag == false);
32 }
33
34 f[0][0] = 1;
35 for(int i = 1; i <= M; i++)
36 for(int j = 0; j < (1 << N); j++)
37 {
38 if(ok[j] && ((m[i] | j) == m[i]))
39 for(int k = 0; k < (1 << N); k++)
40 if((j & k) == 0)
41 f[i][j] = (f[i - 1][k] + f[i][j]) % mod;
42 }
43
44 int ans = 0;
45 for(int i = 0; i < (1 << N); i++)
46 ans = (ans + f[M][i]) % mod;
47 cout<<ans<<endl;
48 return 0;
49 }