【题解】HackerRank - Brick Tiling

题目

https://www.hackerrank.com/challenges/brick-tiling/problem

题意

给定一个有若干个障碍的棋盘,求用 2*3 的 L 型砖头填满的方案数。

题解

  • 很可惜,这并不是插头 dp。
  • 状态就是正在填第 r 行,从当前行开始的前 3 行的当前状态为 a, b, c。
  • 有些像数位 dp,并非记忆化所有状态,而是只记录前 r 行填满,后两行状态为 b, c 的方案数。
  • 最后为了防止方案被重复计算,要求这次必须填第 r 行的最后一个空位。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define FOR(i, x, y) for (decay<decltype(y)>::type i = (x), _##i = (y); i < _##i; ++i)
#define FORD(i, x, y) for (decay<decltype(x)>::type i = (x), _##i = (y); i > _##i; --i)
#ifdef zerol
#define dbg(args...) do { cout << "\033[32;1m" << #args<< " -> "; err(args); } while (0)
#else
#define dbg(...)
#endif
void err() { cout << "\033[39;0m" << endl; }
template<typename T, typename... Args>
void err(T a, Args... args) { cout << a << ' '; err(args...); }
// -----------------------------------------------------------------------------
const int maxm = 1 << 8, maxn = 20 + 5;
const LL MOD = 1E9 + 7;
const int M2[4][2] = {{7, 1}, {7, 4}, {1, 7}, {4, 7}};
const int M3[4][3] = {{1, 1, 3}, {2, 2, 3}, {3, 1, 1}, {3, 2, 2}};
int T, n, m, ALL;
int G[maxn];
char s[maxn];
LL f[maxn][maxm][maxm];

LL go(int r, int a, int b, int c) {
if (a == ALL) {
if (r == n) return 1;
LL& ret = f[r][b][c];
if (ret != -1) return ret;
return ret = go(r + 1, b, c, G[r + 3]);
}
LL ret = 0;
LL target = 1;
while (a & target) target <<= 1;
FOR (i, 0, m - 2)
FOR (t, 0, 4) {
int m1 = M2[t][0] << i;
int m2 = M2[t][1] << i;
if (!(m1 & target) || (a & m1) || (b & m2)) continue;
ret += go(r, a | m1, b | m2, c);
}
FOR (i, 0, m - 1)
FOR (t, 0, 4) {
int m1 = M3[t][0] << i;
int m2 = M3[t][1] << i;
int m3 = M3[t][2] << i;
if (!(m1 & target) || (a & m1) || (b & m2) || (c & m3)) continue;
ret += go(r, a | m1, b | m2, c | m3);
}
return ret % MOD;
}

void init();
int main() {
#ifdef zerol
freopen("in", "r", stdin);
#endif
cin >> T;
while (T--) {
cin >> n >> m;
init();
FOR (i, 0, n) {
scanf("%s", s);
FOR (j, 0, m)
if (s[j] == '#')
G[i] |= 1 << j;
}
G[n] = G[n + 1] = ALL;
cout << go(0, G[0], G[1], G[2]) << endl;
}
}

void init() {
ALL = (1 << m) - 1;
memset(G, 0, sizeof G);
memset(f, -1, sizeof f);
}