完全覆盖 II
时间限制:1000 ms | 内存限制:65535 KB
难度:4
描述
有一天acmj在玩一种游戏----用2*1或1*2的骨牌把m*n的棋盘完全覆盖。但他感觉把棋盘完全覆盖有点简单,他想能不能把完全覆盖的种数求出来?由于游戏难度增加他自己已经没法解决了,于是他希望大家能用程序来帮他把问题解决了。
输入
有多组数据。
每组数据占一行,有两个正整数n(0<n<12),m(0<m<12)。
当n,m等于0时输入结束
输出
每组数据输出占一行,输出完全覆盖的种数。
样例输入
2 2
2 3
2 4
2 11
4 11
0 0
样例输出
2
3
5
144
51205
#include <stdio.h>
#include <string.h>
typedef long long ll;
int n, m;
ll a[15][15];
ll dp[15][1<<12]; // dp[i][j] 表示第i行取状态j的情况数(1-i行均不冲突)
// 1表示横着放 0表示竖着放
bool ok1( int s ) // 行 1不冲突 0冲突
{
int i;
for(i=0;i<n;) {
if(s & (1<<i)) { // 如果第i格横着放
// 如果这个已经是最后一个了 就不能横着放了
if(i==n-1) return 0;
// 如果这个和下一个都是横着放 则继续判断
else if(s & (1<<(i+1))) i+=2;
// 否则就是后一个不为0 则不符合横着放的规则(连续两个1)
else return 0;
}
// 如果是竖着放就留给ok2判断 此时先认为成立
else i++;
}
// 无冲突
return 1;
}
// 这里1是横着放 0是竖着放
// 但为了防止一种不易解释的情况 当前位置出现了一个0
// 无法区分是和上一行合并还是和下一行合并 因此有时会使用一个1来占位
// 所以当上一行是1 这一行不是1 则说明上一行的1 其实是竖着放的0
// 当上一行是0 这一行就需要用1 来占位 表示这个竖着放需要和下一行合并
bool ok2( int s, int ss ) // 上一行状态a和这一行状态b不冲突 1: 不冲突 0: 冲突
{
int i;
for(i=0;i<n;) {
if(s & (1<<i)) { // 如果上一行该位置为1 横着放
// 这一行的状态和上一行无关
if(ss & (1<<i)) { // 这一行该位置横着放 且下一个位置处上下两行都不能是竖着放
if(i==n-1 ||!(s &(1<<i+1))||!(ss &(1<<i+1))) return 0;
else i+=2;
}
// 上一行虽然是1 但是这一行这里不是1 说明这里行上一行的1联合竖着放
else i++;
}
else { // 上一行该位置是竖着放
// 这里对应位置使用了一个1占位 防止0分不清是和上一行对应还是下一行对应
if (ss & (1<<i)) i++;
else return 0;
}
}
return 1;
}
void solve()
{
int i, s, ss;
if ( m < n ) {
int t;
t=n;n=m;m=t;
}
// 根据集合的整数表示[https://blog.csdn.net/hopyGreat/article/details/80992740]
// 0-maxx标记了一行的所有可能表示情况
int maxx = (1<<n)-1;
memset( dp, 0, sizeof(dp) );
for(i=0;i<=maxx;i++)
{
// 取出一个状态判断该行是否发生冲突
// 如果不冲突设置dp[1][i]的值为1
if ( ok1(i) )
dp[1][i] = 1;
}
for(i=2;i<=m;i++) { // 遍历每一行
for(s=0;s<=maxx;s++) { // i-1行的每种状态
for(ss=0;ss<=maxx;ss++) { // i行的每种状态
if (ok2(s,ss)) { //判断第i-1行与第i行情况是否兼容
// 如果不冲突 则第i行状态为ss的有这么多种情况
dp[i][ss]+=dp[i-1][s];
}
}
}
// 保存i行n列的结果数
a[i][n] = a[n][i] = dp[i][maxx];
}
// a[m][n] = a[n][m] = dp[m][maxx];
printf( "%lld\n", a[m][n] );
}
int main()
{
memset( a,-1,sizeof(a) );
while(scanf("%d%d",&m,&n)!=EOF&&n&&m)
{
if ( a[m][n] != -1 ) {
printf( "%lld\n", a[m][n] );
continue;
}
if ( m&1 && n&1 ) {
a[m][n] = a[n][m] = 0;
printf( "%d\n", a[m][n] );
continue;
}
solve();
}
return 0;
}