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万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章