题目链接:
http://acm.sgu.ru/problem.php?contest=0&problem=131
题目大意:
一个N*M的矩形,用格子去将其填满,可以使用的格子有两种。(N<=9 && M<=9)
第1种:1*2的格子
第2种:2*2的格子去掉一个1*1的格子
问一共有多少 种方案。
解题思路:
由于N和M都是小于9的,所以很容易想到状态压缩DP。
状态转移方程:
dp[i][s]+=num[j][s]*dp[i-1][j];
dp[i][s],表示第i行状态为s时的方案数。
第i-1行状态为j,转移到第i行状态为s,由于j—>s的转移方案数不止一种,
所以我们需要用DFS求取j—>s的所有可能。
我的代码能跑46ms,写的比较挫,主要是因为我在DFS的时候枚举很多无效的状态。
源代码:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
#include<iostream>
#include<set>
#include<map>
#include<vector>
#include<algorithm>
#define INF 0x3f3f3f3f
using namespace std;
int n,m;
__int64 dp[9][(1<<10)]; //第i行状态为j时的方案数
int cnt; //总状态的个数
int state;
int ok1(int k) //判断第1行状态是否可取,只允许出现连续2个的1
{
int count=0;
while(k>0)
{
if(k%2==1) count++;
else
{
if(count%2==1) return 0;
else count=0;
}
k=k/2;
}
if(count%2==1) return 0;
return 1;
}
void dfs(int i,int s,int j,int k)
//i表示当前行,s表示当前行的状态,j表示上一行的状态,k表示上一行的第k个格子
//我们现在需要做的就是把上一行的状态j填满
//当前行的第k个格子必定是空的
{
int pre0,pre1,pre2,t0,t1,t2;
if(k>=m) //格子枚举完毕
{
if(j==cnt-1)
dp[i][s]+=dp[i-1][state];
return;
}
pre0=(j&(1<<(k-1))); //上一行的第k-1个格子是否为满
pre1=(j&(1<<k)); //上一行的第k个格子是否为满
pre2=(j&(1<<(k+1))); //上一行的第k+1个格子是否为满
t0=(1<<(k-1));
t1=(1<<k);
t2=(1<<(k+1));
if(pre0 || k==0)
{
dfs(i,s,j,k+1); //不填,这个也有可能导致出现无效状态
}
if(pre1==0)
{
dfs(i,s|t1,j|t1,k+1); //填个竖的
}
if(pre1 && k<=m-2)
{
dfs(i,s|t1|t2,j,k+2); //填个横的,这个时候有可能出现无效状态
}
if(pre1==0 && pre0==0 && k-1>=0)
{
dfs(i,s|t1,j|t1|t0,k+1); //缺左下
}
if(pre1==0 && pre2==0 && k<=m-2)
{
dfs(i,s|t1,j|t1|t2,k+1); //缺右下
}
if(pre1==0 && k<=m-2)
{
dfs(i,s|t1|t2,j|t1,k+2); //缺右上,这个时候也有可能产生无效状态
}
if(pre1 && pre2==0 && k<=m-2)
{
dfs(i,s|t1|t2,j|t2,k+2); //缺左上
}
return;
}
void DP()
{
int i,j,k,t,sum;
sum=0;
for(i=1; i<n; i++) //枚举当前行
{
for(j=0; j<cnt; j++) //枚举上一行的状态
{
if(dp[i-1][j]!=0) //说明i-1行状态j是存在的
{
state=j; //将状态j保存起来
dfs(i,0,j,0);
}
}
}
printf("%I64d\n",dp[n-1][cnt-1]);
return;
}
int main()
{
freopen("in.txt","r",stdin);
int i,j,k,t;
while(scanf("%d%d",&n,&m)==2)
{
i=max(m,n);
j=min(m,n);
n=i,m=j;
memset(dp,0,sizeof(dp));
cnt=(1<<m);
for(i=0; i<cnt; i++)
{
if(ok1(i))
dp[0][i]=1;
}
DP();
}
return 0;
}