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

蒙德里安的梦想

蒙德里安的梦想

思路 状态压缩DP

​ 状态表示: 设\(f[i][j]\)表示前\(i-1\)列已经摆放整齐, 第\(i\)列的状态为\(j\)时的方案数,\(j\)用十进制来表示二进制数,当\(j\)的第\(k\)位为\(0\)时,表示该列没有方块覆盖,即可以理解为该列没有被\(i - 1\)列的\(1 \times 2\)方格覆盖。

​ 集合划分: 第\(i - 1\)列中的\(1 \times 2\)方格摆放已经由状态\(j\)确定, 那么对于\(f[i - 1][k]\), 需要满足\(k \& j = 0\), 对于\(f[i - 1][k]\) 由于前\(i - 2\)列的方格已经摆放整齐,并且第\(i - 2\)列中\(1 \times 2\)方格已经由\(k\)确定, 那么对于未填充的方格,我们只能用\(2 \times 1\)方格来填充, 通过这样的操作,我们便可以从\(f[i - 1][k]\) 转移到\(f[i][j]\)。

​ 状态计算:

\[S = \left \{j \& k = 0 \space and \space even(k) \right \} \\ f[i][j] = \sum _ {k \in S} f[i - 1][k] \\ \]

\(even(k)\) 表示\(k\)没有奇数个连续的\(0\)

Code

#include <iostream>
#include <cstring>
#include <vector>
#define sz(x) (int) x.size()
#define debug(x) std::cout << "debug:" << x << "\n";

using i64 = long long;

const int N = 20, M = 1 << 11;

i64 f[N][M];
std::vector<int> state[M];
bool st[M];

int main() {
	int n, m;
	
	while(std::cin >> n >> m, n || m) {
		for(int i = 0; i < 1 << n; i ++) {
			int cnt = 0;
			bool ok = true;
			for(int j = 0; j < n; j ++) {
				if(i >> j & 1) {
					if(cnt & 1) {
						ok = false;
						break;
					}
					cnt = 0;
				} else cnt ++;
			}	
			if(cnt & 1) ok = false;
			st[i] = ok;
		}

		for(int i = 0; i < 1 << n; i ++) {
			state[i].clear();
			for(int j = 0; j < 1 << n; j ++) {
				if((i & j) == 0 && st[i | j]) {
					state[i].push_back(j);
				}
			}
		}

		memset(f, 0, sizeof f);

		f[0][0] = 1;

		for(int i = 1; i <= m; i ++) {
			for(int j = 0; j < 1 << n; j ++) {
				for(int k = 0; k < sz(state[j]); k ++) {
					f[i][j] += f[i - 1][state[j][k]];
				}
			}
		}

		std::cout << f[m][0] << "\n";
	}
}
posted on 2023-02-28 17:38  Jack404  阅读(17)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3