A: 铺砖块
时间限制: 1 Sec 内存限制: 128 MB
题目描述
现有n*m的一块地板,需要用1*2的砖块去铺满,中间不能留有空隙。问这样方案有多少种
输入
输入n,m(1<=n, m<=11)
有多组输入数据,以m=n=0结束
输出
输出铺砖块的方案数
样例输入
1 2
1 3
1 4
2 2
2 3
2 4
2 11
4 11
0 0
样例输出
1
0
1
2
3
5
144
51205
Solution
因为上面一层的铺法会影响下面一层的决策
所以,这是一道状压DP题目
问题抽象出来就是:用1*2的方块恰好铺满n*m方块的方案数
因为要记录上一层的状态
转化一下就是:铺满前n层(ceng)且第n+1层不铺的方案数
可以用 dp[i][sta] 来表示铺满前i层,第i+1层铺好的状态为sta的方案总数
转移方程就是:
dp[i][sta_to]=sum{dp[i-1][sta_from]}
这里的sta_from和sta_to表示一个状态转移到另一个状态
显然,这些转移状态的单位只有3种
即:0->1 1->0 0 0->0 0 (1表示有砖,0表示没有)
表示铺竖的,不铺,铺横的
所以可以先写一个暴力dfs把所有的状态枚举出来:
void dfs(int t,int from,int to)
{
if (t>m) return;
if (t==m)
{
num++;
change[num][0]=from;
change[num][1]=to;
}
dfs(t+1,from<<1|1,to<<1);
dfs(t+1,from<<1,to<<1|1);
dfs(t+2,from<<2,to<<2);
}
接着就有了转移方程的写法:
dp[i][change[j][1]]+=dp[i-1][change[j][0]];
注意点:
1.要开long long
2.可以与处理出来change数组最多只有13680种状态,节约空间,不用开1000000的数组
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const int maxn=12;
const int maxs=13860;
long long dp[maxn][1<<maxn-1],change[maxs+5][2];
int n,m,num;
void dfs(int t,int from,int to)
{
if (t>m) return;
if (t==m)
{
num++;
change[num][0]=from;
change[num][1]=to;
}
dfs(t+1,from<<1|1,to<<1);
dfs(t+1,from<<1,to<<1|1);
dfs(t+2,from<<2,to<<2);
}
int main()
{
while (cin>>n>>m)
{
if (n==0 && m==0) break;
num=0;
memset(dp,0,sizeof(dp));
memset(change,0,sizeof(change));
dfs(0,0,0);
dp[0][0]=1;
for (int i=1;i<=n;i++)
for (int j=1;j<=num;j++)
dp[i][change[j][1]]+=dp[i-1][change[j][0]];
cout<<dp[n][0]<<endl;
}
return 0;
}