状态压缩 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;
}
posted @   lucky_light  阅读(54)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示