POJ 2411 Mondriaan's Dream (輪廓線dp)

題意:

給你一個NM的棋盤,用12的骨牌填充,問又多少中填充方法

思路:

一開始不知道輪廓線dp,看了大佬們的博客才知道,
輪廓線dp就是用狀壓表示行的狀態,但是這個狀態並不是一行的狀態,而是邊緣的狀態
在這裏插入圖片描述
像這樣dp[i][s],s表示的就是狀態。
知道了這個概念的話,那麼來看這道題,首先,每一個方塊都有兩種可能一種是向下,一種是向右,而如果用0表示在這種狀態下此方塊沒有被覆蓋到,那麼1就表示被覆蓋到了,而且dp肯定是從左到右,從上到下,所以如果dp到第i行,那麼前面的i-1行,肯定就已經算出來結果了.
那麼對於1個方塊就有這麼幾種情況:
1.在這裏插入圖片描述
只能向上,因爲如果不向上,那麼先後循環的時候上方的0是一直不會改變的
2.在這裏插入圖片描述
這個隨意,可以向上,可以向左,可以不填
3.在這裏插入圖片描述
這個也只能向上
4.在這裏插入圖片描述
這個只能跳過不填

現在知道了這些,那麼我們令k1爲< p1 p2 p3 p4 p5 > ,k2爲 < p2 p3 p4 p5 k > k1爲舊的狀態,而k2是新的狀態,那麼k2可以由k1遞推而來
如果k1的頭爲1的話,可以直接跳過

if (state & (1 << (M-1))) 
	dp[curr][(state << 1) ^ (1 << M)] += dp[1-curr][state];

如果k1的頭0並且層數大於1的話,那麼可以向上填充

if (i && !(state & (1 << (M-1)))) 
	dp[curr][(state << 1) ^ 1] += dp[1-curr][state];

從如果k1的尾巴爲0的話,那麼可以向左填充

 if (j && (state & (1 << (M-1))) && !(state & 1))
 	 dp[curr][(state << 1) ^ (1 << M) ^ 3] += dp[1-curr][state];

這樣遞推就完成了

AC代碼:

#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int inf = 0x3f3f3f3f;
const int maxn = 1e5 + 5;
const int Mod = 1e9 + 7;

int dp[2][1<<5];
int n, m, cur;

void updata(int a, int b) {
    if(b & (1<<m)) dp[cur][b^(1<<m)] = dp[cur][b^(1<<m)] + dp[1-cur][a];
}

int main(int argc, char const *argv[]) {
    whhile(scanf("%d %d", &n, &m) != EOF && (n || m)) {
	    if(m > n) swap(n, m);
	    memset(dp, 0, sizeof(dp));
	    cur = 0;
	    dp[cur][(1<<m)-1] = 1;
	    for (int i = 0; i < n; i ++) {
	        for (int j = 0; j < m; j ++) {
	            cur ^= 1;
	            memset(dp[cur], 0, sizeof(dp[cur]));
	            for (int k = 0; k < (1<<m); k ++) {
	                updata(k, k<<1);
	                if(i && !(k & (1<<(m-1)))) updata(k, (k<<1)^(1<<m)^1);
	                if(j && (!(k & 1))) updata(k, (k<<1) ^ 3);
	            }
	        }
	    }
	    printf("%d", dp[cur][(1<<m)-1]);
    }
    return 0;
}

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