广场铺砖问题

题意:

给你n * m的棋盘,问用1 * 2的棋子全部铺满的方案数。n,m <= 11

解:

裸的状压DP。

我们就设f[i][j]表示第i行状态是j,前面i - 1行全部放满的方案数。

转移的时候枚举i - 1行的状态,然后还是DFS转移。

DFS的时候,如果i - 1行为空,那这里必须竖着放。

否则如果前一格为空的话,可以横着放。否则只能不放。

然后把这一行搜到最后的时候更新一下。

预处理是直接搞出第一行的所有可行状态。(update:只要设f[0][lm - 1] = 1即可)

算是比较基础的吧。

 1 #include <cstdio>
 2 
 3 typedef long long LL;
 4 const int N = 12;
 5 
 6 LL f[N][1 << N];
 7 int n, m;
 8 
 9 inline bool check(int s) {
10     bool f = 0;
11     for(int i = 0; i < m; i++) {
12         if((s >> i) & 1) {
13             f ^= 1;
14         }
15         else {
16             if(f) {
17                 return 0;
18             }
19         }
20     }
21     return !f;
22 }
23 
24 LL ans;
25 int state;
26 void DFS(int x, int y, int ns) {
27     if(y == m) {
28         f[x][ns] += ans;
29         return;
30     }
31     if(y > m) {
32         return;
33     }
34     if(!((state >> y) & 1)) {
35         DFS(x, y + 1, ns | (1 << y));
36         return;
37     }
38     DFS(x, y + 1, ns);
39     if(y && (!((ns >> (y - 1)) & 1))) {
40         DFS(x, y + 1, ns | (1 << y) | (1 << (y - 1)));
41     }
42     return;
43 }
44 
45 int main() {
46     scanf("%d%d", &n, &m);
47 
48     int lm = 1 << m;
49 
50     for(int i = 0; i < lm; i++) {
51         f[1][i] = check(i);
52     }
53 
54     for(int i = 2; i <= n; i++) {
55         for(int j = 0; j < lm; j++) { // state of i - 1
56             //printf("f[%d][%d] = %lld \n", i - 1, j, f[i - 1][j]);
57             ans = f[i - 1][j];
58             state = j;
59             DFS(i, 0, 0);
60         }
61     }
62     printf("%lld", f[n][lm - 1]);
63     return 0;
64 }
AC代码

 

posted @ 2018-09-28 18:55  garage  阅读(103)  评论(0编辑  收藏  举报