1. 程式人生 > >【牛客 - 301哈爾濱理工大學軟體與微電子學院第八屆程式設計競賽同步賽(高年級)】小樂樂搭積木(狀壓dp)

【牛客 - 301哈爾濱理工大學軟體與微電子學院第八屆程式設計競賽同步賽(高年級)】小樂樂搭積木(狀壓dp)

題幹:
 

小樂樂想要給自己搭建一個積木城堡。

積木城堡我們假設為n*m的平面矩形。

小樂樂現在手裡有1*2,2*1兩種地磚。

小樂樂想知道自己有多少種組合方案。

 

輸入描述:

第一行輸入整數n,m。(1<=n,m<=10)

輸出描述:

輸出組合方案數。

示例1

輸入

複製

2 3

輸出

複製

3

說明

 
 

示例2

輸入

複製

1 3

輸出

複製

0

示例3

輸入

複製

2 5

輸出

複製

8

解題報告:

   這題狀壓dp,,那種很傳統的方法就不貼了,,今天來貼一個更快的方法、、不同點不在於預處理第一行,而是下面的行,也就是直接找到第二行所有符合的狀態,順便找到對應上一行應該有的狀態,直接做和就可以了。

AC程式碼:

/*優化:不去盲目的列舉所有狀態i和j然後判斷狀態j能否到達i,這樣效率很低,因為能到達i的狀態j很少
因此對於每種狀態i,由i區搜尋能到達i的狀態j,大大提高效率
有298ms->32ms
*/
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <queue>
#include <algorithm>
#include <map>
#include <cmath>
#include <iomanip>
#define INF 0x3f3f3f3f
typedef long long LL;
using namespace std;
  
const int MAX=(1<<11)+10;
int n,m;
LL temp[MAX],dp[MAX],biao[15];
bool check(int i){
    while(i){
        if(i&1){
            i>>=1;
            if(!(i&1))return false;//第j列是1則第j+1列必須是1
            i>>=1;//繼續判斷下一列
        }else i>>=1;//繼續判斷下一列
    }
    return true;
}
void Init(){
    memset(temp,0,sizeof temp);
    for(int i=0;i<biao[m];++i)if(check(i))temp[i]=1;//初始化第一行
}
void dfs(int lie,int now,int cur) {
	if(lie == m) {
		dp[now] += temp[cur];return ;
	}
	if((now>>lie) & 1) {
		dfs(lie+1,now,cur);
		if((now>>(lie+1)) & 1) dfs(lie+2,now,cur|(1<<lie)|(1<<(lie+1)));
	}
	else dfs(lie+1,now,cur|(1<<lie));
}
void DP(){
    for(int k=2;k<=n;++k){
        for(int i=0;i<biao[m];++i) dp[i]=0;
        for(int i=0;i<biao[m];++i) dfs(0,i,0);
        for(int i=0;i<biao[m];++i) temp[i]=dp[i];
    }
}
  
int main(){
    biao[0]=1;
    for(int i=1;i<12;++i)biao[i]=2*biao[i-1];
    scanf("%d%d",&n,&m);
    //if(n<m)swap(n,m);//始終保持m<n,提高效率
    Init();
    DP();
    printf("%lld\n",temp[biao[m]-1]);//輸出最後一行到達時的狀態必須全部是1
    return 0;
}