1. 程式人生 > >poj2411 Mondriaan's Dream (狀壓dp+多米諾骨牌問題)

poj2411 Mondriaan's Dream (狀壓dp+多米諾骨牌問題)

這道題的解析這個部落格寫得很好

大致意思就是我們可以只處理兩行之間的關係,然後通過這兩個關係推出所有行(有點像矩陣快速冪的思想)

幾個要注意的地方

(1)第0行為全1

(2)發現自己的思維習慣還是先行在狀態,我自己寫得時候老是寫反。

(3)path的個數可能有很多,不只是1<<n,可以輸入極限資料然後輸出路徑的數目作為陣列空間大小

(4)拿小的作列

(5)這道題是人為的設定一種方式,使得二進位制與骨牌是一一對應的

如果是橫放,就1 1 如果是豎放就 0 如果不放就是 1

                         11                        1                        0

然後這裡的二進位制操作非常的秀,要認真學習

#include<cstdio>
#include<cstring>
#include<algorithm>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;

typedef long long ll;
const int MAXN = 15;
ll dp[MAXN][2100];
int path[14000][2], p, n, m;

void dfs(int l, int now, int pre)
{
	if(l > m) return;
	if(l == m)
	{
		path[p][0] = pre;
		path[p++][1] = now;
		return;
	}
	
	dfs(l + 2, (now << 2) | 3, (pre << 2) | 3);
	dfs(l + 1, (now << 1) | 1, pre << 1);
	dfs(l + 1, now << 1, (pre << 1) | 1);
}

int main()
{
	while(~scanf("%d%d", &n, &m) && n)
	{
		memset(dp, 0, sizeof(dp));
		if(m > n) swap(n, m);
		p = 0;
		dfs(0, 0, 0);
		
		dp[0][(1<<m)-1] = 1;
		_for(i, 1, n)
			REP(j, 0, p)
				dp[i][path[j][1]] += dp[i-1][path[j][0]];
		printf("%lld\n", dp[n][(1<<m)-1]);
	}
	
	return 0;
}