• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
jacklee404
Never Stop!
博客园    首页    新随笔    联系   管理    订阅  订阅
搭积木-记忆化搜索-区间DP

搭积木

题目

搭积木

思路

​ 首先理一下题目,需要注意以下:

  1. 从给定的图自底向上摆积木, 不摆是一种方案,并且m个积木已经作为地基
  2. 摆放的时候保证下方有积木,并且连续摆放,不能有缝隙
  3. ‘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]\)。

​ 为什么那, 画个图:

​ image-20230607205049153

​ 考虑上面的图, \(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];
}
posted on 2023-06-07 20:55  Jack404  阅读(14)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3