POJ2663

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;
}


  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值