显然是一个状压DP。
将方格的摆放分成两种:
1.水平摆放:此时所占的两个格子都记为1。
2.竖直摆放:此时底下那个格子记为1,上面那个记为0。
这样的话,每行都会有一个状态表示。
定义:dp[i][s]表示考虑已经填到第i行,这一行状态为s的方法数
转移:dp[i][s] = dp[i][s]+dp[i-1][s‘]
(s‘为上一行的状态,当第i行和第i-1行能够满足条件时,进行转移)
先预处理出所有满足条件的第一行,然后从第二行开始转移。
最后答案为dp[n][(1<<m)-1].
当n<m时交换n和m可减小1<<m,即减少状态数。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#define Mod 1000000007
#define ll long long
using namespace std;
#define N 2100ll dp[13][N];
int n,m;int FirstLine(int state)
{
int i=0;
while(i<m)
{
if(state & (1<<i)) //第i列为1,第i+1列也存在且必须为1
{
if(i < m-1)
{
if(state & (1<<(i+1))) //第i+1列为1
i += 2;
else
return 0;
}
else
return 0;
}
else
i++;
}
return 1;
}int Can(int ka,int kb) //ka:这一行,kb:上一行
{
int i = 0;
while(i<m)
{
if(ka & (1<<i)) //这一行i列为1
{
if(kb & (1<<i)) //如果上一行i列为1,则为两个水平块
{
if(i < m-1 && (ka & (1<<(i+1))) && (kb & (1<<(i+1))))
i += 2;
else
return 0;
}
else //上一行为0,竖着放的
i++;
}
else //这一行i列为0,上一行i列必须填充
{
if(kb & (1<<i))
i++;
else
return 0;
}
}
return 1;
}int main()
{
int i,j,sa;
int state1,state2;
while(scanf("%d%d",&n,&m)!=EOF)
{
if((n*m)%2)
{
puts("0");
continue;
}
memset(dp,0,sizeof(dp));
if(n < m)
swap(n,m);
int MAX = (1<<m)-1;
for(sa=0;sa<=MAX;sa++)
{
if(FirstLine(sa)) //此状态可以作为第一行的状态
dp[0][sa] = 1;
}
for(i=1;i<n;i++) //行递增
{
for(state1=0;state1<=MAX;state1++)
{
for(state2=0;state2<=MAX;state2++)
{
if(Can(state1,state2))
dp[i][state1] += dp[i-1][state2];
}
}
}
printf("%lld\n",dp[n-1][MAX]);
}
return 0;
}
UESTC 885 方老师买表