搭积木
题目
思路
首先理一下题目,需要注意以下:
- 从给定的图自底向上摆积木, 不摆是一种方案,并且m个积木已经作为地基
- 摆放的时候保证下方有积木,并且连续摆放,不能有缝隙
- ‘X’表示该点不能摆放
设\(f[i][l][r]\)表示前\(i - 1\)层已经摆放好,第\(i\)层在\([l, r]\)这段区间内连续摆放小方块的方案数量。
那么有,\(f[i][l][r] = \sum \limits_{l \le x\le y\le r}f[i][x][y]\)。
为什么那, 画个图:
考虑上面的图, \(f[1][l][r]\) 一定是从\(f[2][x][y]\), 且\(x, y\)一定是在\([l, r]\)内转移的, 因为题目的要求保证了金字塔形状, 我们倒着推,一定是从更小的区域转移过来的。
由于方程转移需要还没有求出的状态,我们可以用记忆化搜索来推出所有的状态。
#include <bits/stdc++.h>
using i64 = long long;
const int N = 110, mod = 1e9 + 7;
int n, m;
char s[N][N];
int f[N][N][N];
/*
f[i][l][r] 表示已经摆好i - 1 ~ n, 在区间[l, r] 内连续摆放一段积木的方案数
*/
int dfs(int dep, int l, int r) {
if (dep == 0) return 1;
if (f[dep][l][r] != -1) return f[dep][l][r];
i64 ans = 1;
for (int i = l; i <= r; i ++) {
if (s[dep][i] == '.') {
for (int j = i; j <= r; j ++) {
// 若区间内有'X' break
if (s[dep][j] == 'X') {
break;
}
ans = (ans + dfs(dep - 1, i, j)) % mod;
}
}
}
f[dep][l][r] = ans;
return f[dep][l][r];
}
int main() {
std::cin >> n >> m;
for (int i = 1; i <= n; i ++) {
std::cin >> s[i] + 1;
}
memset(f, -1, sizeof f);
dfs(n, 1, m);
std::cout << f[n][1][m];
}