《算法競賽進階指南》 第六章 291. 蒙德里安的夢想 狀態壓縮DP

https://www.acwing.com/problem/content/293/

求把 N×M 的棋盤分割成若干個 1×2 的長方形,有多少種方案。
例如當 N=2,M=4 時,共有 5 種方案。當 N=2,M=3時,共有 3 種方案。
如下圖所示:

輸入格式
輸入包含多組測試用例。

每組測試用例佔一行,包含兩個整數 N和 M。

當輸入用例 N=0,M=0時,表示輸入終止,且該用例無需處理。

輸出格式
每個測試用例輸出一個結果,每個結果佔一行。

數據範圍
1≤N,M≤11
輸入樣例:
1 2
1 3
1 4
2 2
2 3
2 4
2 11
4 11
0 0
輸出樣例:
1
0
1
2
3
5
144
51205

解答
使用數字的二進制表示方塊的擺放
1 則表示 豎直放置,下一行對應的列 則需要標記爲0 表示不放纔可行
0 表示不放 上一行對應的列是豎放的 或者表示橫放的一部分。
所以拋開上一行的影響(curr | prev),必須偶數個0連在一起。
同時兩行相同的位置上不應該出現兩個1(curr&prev)

代碼如下 TLE了

// 291. 蒙德里安的夢想 jichu.cpp : 此文件包含 "main" 函數。程序執行將在此處開始並結束。
//

#include <iostream>
#include <assert.h>
#include <cstring>

using namespace std;
/*
https://www.acwing.com/problem/content/293/
求把 N×M 的棋盤分割成若干個 1×2 的長方形,有多少種方案。
例如當 N=2,M=4 時,共有 5 種方案。當 N=2,M=3 時,共有 3 種方案。

如下圖所示:
2411_1.jpg

輸入格式
輸入包含多組測試用例。
每組測試用例佔一行,包含兩個整數 N 和 M。
當輸入用例 N=0,M=0 時,表示輸入終止,且該用例無需處理。

輸出格式
每個測試用例輸出一個結果,每個結果佔一行。

數據範圍
1≤N,M≤11
輸入樣例:
1 2
1 3
1 4
2 2
2 3
2 4
2 11
4 11
0 0
輸出樣例:
1
0
1
2
3
5
144
51205


1 2
2 2
3 2
*/

const int N = 11;
int dp[N+5][1<<N];
int n, m;

int getN(int state, int idx) {
	return (state >> idx) & 1;
}

bool check1(int state) {
	for (int i = 0; i < m;) {
		if (getN(state, i) == 0 && (i == m - 1 || getN(state, i + 1) == 1)) {
			return false;
		}else if (getN(state, i) == 0 && getN(state, i + 1) == 0) {
			i = i + 2;
		}else if (getN(state, i) == 1) {
			i++;
		}else{
			assert(0);
		}
	}

	return true;
}


bool check2(int prev, int curr) {
	int state = prev & curr;
	if (state != 0) return false;
	if (check1(prev | curr) == false) return false;
	return true;
}


int main()
{
	while (cin >> n >> m) {
		if (n == 0 && m == 0) break;
		memset(dp, 0, sizeof dp);
		dp[0][0] = 1;
		for (int i = 0; i <= n; i++) {
			int debug = 0;
			for (int curr = 0; curr < 1 << m; curr++) {
				//檢查當前狀態是否合法
				//if (check1(curr) == false) { continue; }
				for (int next = 0; next < 1 << m; next++) {
					//檢查下一行的狀態能和當前匹配
					//那麼下一行狀態增加對應的方案數
					if (check2(curr, next) == true) {
						dp[i + 1][next] += dp[i][curr];
					}
				}
			}
		}

		cout << dp[n][0] << endl;
	}

	return 0;
}

我的視頻空間

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章