状态压缩 DP:蒙德里安的梦想
C++
蒙德里安的梦想
/* 问题描述: 求把 N×M 的棋盘分割成若干个 1×2 的长方形,有多少种方案。 例如当 N=2,M=4 时,共有 5 种方案。当 N=2,M=3 时,共有 3 种方案。 数据范围: 1 ≤ N,M ≤ 11 解题思路: 本题是一个状态压缩的动态规划问题,相邻状态之间存在着递进的关系。 f[i][state] 表示第 i 列 state 状态下的方案数,state 是一个二进制的状态编码,该二进制编码 对应的是 0 - j 行的状态,1 表示这是一个横着放长方形的起始点。 那么,f[i-1][state0] -> f[i][state1] 合法的方案为: condition1: (state0 & state1) == 0,这是为了保证横着放的长方形版块不会冲突 condition2: state = (state1 | state2),这样 state 必须保证自身 0 是连续的偶数,因为我们要竖着放板子了。 那么在循环过程着中,state0 和 state1 如何配对: plan1. 暴力枚举,然后使用 condition1 和 condition2 筛选掉不合法的方案 plan2. 拿到 state2,dfs寻找合适的 state1,但是这样可能会爆栈,dfs快在前面不合法,后面就可以剪枝,不搜。 考虑到爆栈,我们细谈 plan1,首先是对列 i 遍历,从小到大,进而遍历 state1,遍历寻找合法的 state0 ,加到当前位置 N * (2 ^ N) * (2 ^ N) * (判断state0 state1合法的复杂度) 考虑判断合法复杂度的问题,condition1 -> O(1) condition2 -> O(N),但是状态不多,完全可以打表完成,因此优化到 O(1) --> 最终复杂度 O(N * 2 ^ (2N)) 大概 1e7 不妨再想一想, 对于 state1 完全也可以是打一个 state0 的表格,毕竟他是重复计算了最外层循环 N 次 注意点: 全部 Long Long,输入输出,函数定义 打表是个好东西 */ #include <cstdio> #include <iostream> #include <cstring> #include <algorithm> #include <cmath> #include <vector> #include <queue> using namespace std; typedef long long LL; const int N = 12; int n, m; LL f[N][1 << N]; int st[1 << N]; // 用于记录状态的有效性 LL solution() { // 首先打表 st[] int continuous_zero_cnt = 0; for (int state = 0; state < (1 << n); state ++ ) { continuous_zero_cnt = 0; st[state] = true; for (int j = 0; j < n; j ++ ) { if (((state >> j) & 1) == 0) { continuous_zero_cnt += 1; } else { if ((continuous_zero_cnt & 1) == 1) { // odd continuous cnt st[state] = false; } continuous_zero_cnt = 0; } } if ((continuous_zero_cnt & 1) == 1) { // odd continuous cnt st[state] = false; } } // 打表合法的 state1 -> state0 vector<int> legal_state_match[1 << N]; for (int state1 = 0; state1 < (1 << n); state1 ++ ) { for (int state0 = 0; state0 < (1 << n); state0 ++ ) { if ((state0 & state1) == 0 && st[state0 | state1]) { legal_state_match[state1].push_back(state0); } } } // dp initialize memset(f, 0, sizeof f); f[0][0] = 1; for (int i = 1; i <= m; i ++ ) { for (int state1 = 0; state1 < (1 << n); state1 ++ ) { for (auto state0 : legal_state_match[state1]) { f[i][state1] += f[i-1][state0]; } } } return f[m][0]; } int main() { long long res; while (scanf("%d%d", &n, &m), n != 0 || m != 0) { res = solution(); printf("%lld\n", res); } return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)