POJ2663 Tri Tiling
现在给出一个3*n(0<=n<=30)的矩阵,要求用1*2的骨牌来填充,问有多少种填充方式?
分析:
解法一:
首先这道题n的规模比较小我先试下用轮廓线DP来解。
(轮廓线DP见:http://blog.csdn.net/u013480600/article/details/19499899)
注意POJ的这道题目测试数据当n==0时输出的是1,否则WA。
AC代码:47MS
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn = 15;
long long d[2][1<<maxn];
int n,m,cur;
void update(int a,int b)//a是包含m位1进制数的老状态,b是包含m+1位1进制数的新状态
{
if(b&(1<<m)) d[cur][b^(1<<m)] += d[1-cur][a];//只有新轮廓线首位为1时才更新
}
int main()
{
while( scanf("%d",&n)==1&&n>=0 )
{
//if(m>n)swap(n,m);
m=3;
memset(d,0,sizeof(d));
cur=0;
d[cur][(1<<m)-1]=1;
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
{
cur ^=1;
memset(d[cur],0,sizeof(d[cur]));
for(int k=0;k<(1<<m);k++)//k的二进制形式表示前一个格子的轮廓线状态
{
update(k,k<<1);//当前格不放,直接k左移一位就表示带m+1位的新轮廓线的状态
if(i && !(k&(1<<(m-1)) )) update(k,(k<<1)^(1<<m)^1);//上放,要求轮廓线首为0
if(j && (!(k&1)) ) update(k,(k<<1)^3);//左放,要求轮廓线尾0,首1
}
}
printf("%I64d\n",d[cur][(1<<m)-1]);
}
return 0;
}
解法二:递推法。
由于本题的矩阵只有3列,所以可以推断出,该矩阵必定由且仅由3*2,3*4,…3*m(m为偶数)的不可分割的小矩阵构成。如下:
等等。还有2*6,2*8的矩阵没有画出来,可以自己想想如何构成不可分割的2*6小矩阵。且可以推断2*m(m>=4且为偶数)的不可分割小矩阵数目都是2.想想为什么?
所以可以得到计算a[n](表示3*n的矩阵有多少摆放方式)的递推公式:
a[n]=a[n-2]*3+a[n-4]*2+a[n-6]*2+….初始值为a[0]=1且其他a[n]初始时都为0。
AC代码:0MS
//利用递推的公式求解
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn = 35;
int n;
long long a[maxn];
int main()
{
memset(a,0,sizeof(a));
a[0]=1;
a[2]=3;
for(int i=4;i<31;i+=2)
{
int j=4;
a[i] +=a[i-2]*3;
while(j<=i)
{
a[i]+=a[i-j]*2;
j+=2;
}
}
while(scanf("%d",&n)==1&&n>=0)
{
printf("%I64d\n",a[n]);
}
return 0;
}
解法三:利用兼容模式队求解状态压缩DP。
(兼容模式状态压缩DP见:http://blog.csdn.net/u013480600/article/details/19569291)
直接把原题的m该成3,然后注意读入的格式即可。
AC代码:0ms
//兼容模式队求解状态压缩DP
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 35;//最大行数
const int maxm = 16*16;//兼容模式对的最大数目
int n,m,w;
long long d[maxn][1<<4];
int path[maxm][2];
void dfs(int c,int pre,int now)
{
if(c>m)return ;
else if(c==m)
{
path[w][0]=pre;
path[w++][1]=now;
return ;
}
dfs(c+1,(pre<<1)|1,now<<1);//不放
dfs(c+1,(pre<<1),(now<<1)|1);//上放
dfs(c+2,(pre<<2)|3,(now<<2)|3);//右放
}
int main()
{
while(scanf("%d",&n)==1&&n>=0)
{
m=3;
w=0;
dfs(0,0,0);
memset(d,0,sizeof(d));
d[0][(1<<m)-1]=1;
for(int i=0;i<n;i++)
for(int j=0;j<w;j++)
d[i+1][path[j][1]] += d[i][path[j][0]];
printf("%I64d\n",d[n][(1<<m)-1]);
}
return 0;
}