题目大意
用1*2的骨牌覆盖n*m的棋盘,问有多少种方案。可以旋转90°放。不能覆盖则输出0。
解题思路
用 f【i,s】表示 把前 i-1 行覆盖满、第 i 行覆盖状态为 s 的覆盖方案数。因为在第 i 行上放置的骨牌多也只能影响到第 i-1 行(竖着放的时候是占用当前行和上一行),
因此要满足在填第i行时,第1~i-2行已经全部填满,所以
DFS一行的状态,要使得填完(不一定填满)第i行时,第i-1行被填满。则容易得递推式:
f[i][s] += f[i-1][s2] f[0][1<<m-1]=1;
然后就是处理如何dfs的问题。参数:列p,当前行的状态now,上一行的状态last。那么有三种操作
①竖放:dfs(p+1,(now<<1)|1,last<<1) ; //竖放(占据当前行和上一行),在当前行竖放需要上一行的p+1列是空的
②横放:dfs(p+2,(now<<2)|3,(last<<2)|3) ; //横放:上一行当前列没有空位,没办法竖放
③不放:dfs(p+1,(now<<1),(last<<1)|1); //不放 : 前一行的p+1列没有空位,就不能放
(注* 以①为例,表示当前行在p+1列的时候,状态为(now<<1)|1,上一行状态为last<<1,与之前不同的是这次是从左往右找列,也就是走到下一列就让前面的向左移,腾出一个未定义的列使用)
代码
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cstdio>
using namespace std;
int n,m,row;
long long f[12][1<<12];//这里要开longlong
void dfs(int p,int now,int last)
{
if(p>m) return ;
if(p==m)//到达最后一列了
{
f[row][now] += f[row-1][last];
}
dfs(p+1,(now<<1),(last<<1)|1); //不放 : 前一行的p+1列没有空位,就不能放
dfs(p+1,(now<<1)|1,last<<1);//竖放(占据当前行和上一行),在当前行竖放需要上一行的p+1列是空的
dfs(p+2,(now<<2)|3,(last<<2)|3);//横放:上一行当前列没有空位,没办法竖放
}
int main()
{
while(scanf("%d%d",&n,&m))//n行m列
{
if(n==0 && m==0) break;
if(n%2 && m%2)
{
printf("0\n");
continue;
}
if(n<m) swap(n,m);//让小的当列
memset(f,0,sizeof(f));
f[0][(1<<m)-1] = 1; //第0行全放满是一种方案
for(row=1;row<=n;++row)
dfs(0,0,0);
cout<<f[n][(1<<m)-1]<<endl;
}
return 0;
}
题目大意
用1*2的骨牌覆盖3*n的棋盘,问有多少种方案
解题思路
这道题和上一道题没什么太大区别,就是把m变成3,还是和好写的
代码
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cstdio>
using namespace std;
int n;//n行3列
int f[35][1<<3];
int row;
void dfs(int p,int now,int last)
{
if(p>3) return ;
if(p==3)
{
f[row][now] +=f[row-1][last];
}
else
{
dfs(p+1,now<<1,(last<<1)|1);//不放
dfs(p+2,(now<<2)|3,(last<<2)|3);//横放
dfs(p+1,(now<<1)|1,last<<1);
}
}
int main()
{
while(scanf("%d",&n) && n !=-1)
{
if(n%2) {printf("0\n"); continue;}
memset(f,0,sizeof(f));
f[0][(1<<3)-1]=1;
for(row=1;row<=n;++row)
{
dfs(0,0,0);
}
cout<<f[n][(1<<3)-1]<<endl;
}
return 0;
}