hihoCoder 狀態壓縮二 狀態壓縮dp 入門題 4

描述

歷經千辛萬苦,小Hi和小Ho終於到達了舉辦美食節的城市!雖然人山人海,但小Hi和小Ho仍然抑制不住興奮之情,他們放下行李便投入到了美食節的活動當中。美食節的各個攤位上各自有着非常多的有意思的小遊戲,其中一個便是這樣子的:

小Hi和小Ho領到了一個大小爲N*M的長方形盤子,他們可以用這個盒子來裝一些大小爲2*1的蛋糕。但是根據要求,他們一定要將這個盤子裝的滿滿的,一點縫隙也不能留下來,才能夠將這些蛋糕帶走。

這麼簡單的問題自然難不倒小Hi和小Ho,於是他們很快的就拿着蛋糕離開了~

但小Ho卻不只滿足於此,於是他提出了一個問題——他們有多少種方案來裝滿這個N*M的盤子呢?

值得注意的是,這個長方形盤子的上下左右是有區別的,如在N=4, M=3的時候,下面的兩種方案被視爲不同的兩種方案哦!

 

輸入

每個測試點(輸入文件)有且僅有一組測試數據。

每組測試數據的第一行爲兩個正整數N、M,表示小Hi和小Ho拿到的盤子的大小。

對於100%的數據,滿足2<=N<=1000, 3<=m<=5。

輸出

考慮到總的方案數可能非常大,只需要輸出方案數除以1000000007的餘數。

 

樣例輸入

2 4

樣例輸出

5

 

解題思路:看到n,m兩個的數據範圍不太一樣,一個很大,一個很小,可以推斷出來這是一道狀壓題。它有兩種蛋糕 1*2 or 2*1,

 對於第i行第j列(i,j)這個位置,如果我在這裏取的話我可以用1表示,如果不取的話 那我就可以用0表示。

而且我在第i行怎麼放,只會影響到i+1行,

那麼我們就可以知道 1~i-1一定是放好的,i+2~n行一定是沒放的。

這樣的話 我們就可以構造一個狀態轉移方程的雛形。

但是橫着取還是豎着取該怎麼區分尼,還有就是怎麼樣判斷狀態是否兼容。

如果是橫着放的話,那麼在(i,j)(i,j+1)這兩個位置 我就都標記爲1,

如果是豎着放的話,那麼我就在(i,j+1)的位置標記爲1, (i,j)的位置標記爲0

舉個例子

6   0110    在第2個和第3個位置有一個1,那麼我在2 3位置一定橫放了一個蛋糕。那麼這裏的0該怎麼辦尼,留給下一行。

比如這種情況

行          狀態

i               6             0110

i+1           9             1001

那麼我們可以發現這兩行之間的狀態是兼容的,我在第i行沒有使用的位置被i+1行的狀態給使用了。

行          狀態

i               6             0110

i+1          17            1111

這樣的狀態也是兼容的。

說到這,應該思路都很清晰了,那麼我可以先預處理出第一行狀態合理的情況。之後我只需要往下推,枚舉第i行和第i-1行的狀態是否兼容就行。

 

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define ll long long
const int maxn=1010;
const int mod=1e9+7;
int n,m;
ll dp[maxn][1<<5];
bool check(int i,int m){
	int j=0;
	while(j<m){
		if(!(i&(1<<j))) j++;
		else if(j==m-1||!(i&(1<<(j+1)))) return false;
		else j+=2;
	}
	return true;
}
bool jud(int sta,int stb,int m){
	int j=0;
	while(j<m){
		if(!(sta&(1<<j))){
			if(!(stb&(1<<j))) return false;
			j++;
		}
		else{
			if(!(stb&(1<<j))) j++;
			else if(j==m-1||!((sta&(1<<(j+1)))&&(stb&(1<<(j+1))))) return false;
			else j+=2;
		}
	}
	return true;
}
int main(){
	int i,j,k;
	scanf("%d%d",&n,&m);
	if(n<m) swap(n,m);
	int  x=1<<m;
	for(i=0;i<x;i++){
		if(check(i,m)) dp[1][i]=1;
	}
	for(i=2;i<=n;i++){
		for(j=0;j<x;j++){
			for(k=0;k<x;k++){
				if(jud(j,k,m))
					dp[i][j]=(dp[i][j]+dp[i-1][k])%mod;
			}
		}
	}
	printf("%lld\n",dp[n][x-1]);
	return 0;
}

 

發佈了79 篇原創文章 · 獲贊 12 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章