[2022.1.13]UPC-2021级新生个人训练赛第22场-9783 Problem H 铺地砖

问题 H: 铺地砖

时间限制: 1.000 Sec 内存限制: 128 M

题目描述

一天,晨晨的数学老师布置了一道题目,大意如下:用1×1和2×2的磁砖不重叠地铺满n×3的地板,共有多少种方案?
例如:n=1时:1×3的地板方法就一个,直接由三个1×1的磁砖铺满。
      n=2时:2×3的地板可以由下面3种方案铺满:

输入

第一行:一个整数n(1≤n≤100)。

输出

输出铺满n×3的地板的方案数。

样例输入

3

样例输出

5

提示

对于20%的数据,1≤n≤15;
对于50%的数据,1≤n≤30;
对于100%的数据,1≤n≤100;

题解:

这个题!!!

找到规律还没有结束!居然当n为100时会超出long long int的表示范围...

所以还要用上“高精度”? 用数组来储存超大数的每一位,并进行运算。

那...我们先来找规律

我想了好一会儿,基本上这种题都需要用到“递推”的思想,那我们来想想看,一个n行3列的地板,不就是在n-1行3列的地板上再加了1行嘛。

我们不妨设想额外加的这一行是由1*1的地砖铺满的,用M[n]表示n行3列地板有多少种铺法。

那么 M[n]=M[n-1]+“未知数x”

没有算出来“x” 是第n行和第n-1行中有2*2地砖的铺法。

我们把2*2地砖看作由4个1*1地砖组合而来

第n行和第n-1行如果要想有2*2的地砖,也即代表着第n-1行和第n行要有4个紧邻的1*1的地砖。

也就代表着.....

第n-1行也必须全是1*1地砖!

通过题目描述我们看到 2行3列有3种铺法,其中有2*2地砖的铺法是2种。

所以这个“x” 是  n-1行3列地板中第n-1行全为1*1地砖时的总铺法 再乘以2。

n-1行3列地板中第n-1行全为1*1地砖时的总铺法  ? 哇,我迷糊了, 而这个实际上可以转化为求 n-2行3列地板的总铺法呀,  因为第n-1行的铺法是固定了的。

答案在这里呼之欲出啦,我们求出来了!

M[n] = M[n-1] + M[n-2]*2

所以,我立马写了这样的一段代码:

#include <cstdio>
typedef long long int ll;
using namespace std;
ll dp[110];
int main(){
    dp[0]=1;dp[1]=1;dp[2]=3;
    ll n,i;
    scanf("%lld",&n);
    for(i=2;i<=n;i++)
        dp[i]=dp[i-1]+dp[i-2]*2;
    printf("%lld",dp[n]);
}

82/100  : (

想要过剩下的测试案例,还需要用上高精度。

所以...我们来看AC代码吧?

#include <cstdio>
typedef long long int ll;
using namespace std;
int dp[110];
void plu(int *a,int *b,int *c){     //用数组实现两个大数相加,结果储存在*c指向的数组
    int jw=0;
    while(*a!=-1&&*b!=-1){
        *c=(*a+*b+jw)%10;
        jw=(*a+*b+jw)/10;
        a++;
        b++;
        c++;
    }
    while(*a!=-1){
        *c=(*a+jw)%10;
        jw=(*a+jw)/10;
        c++;
        a++;
    }
    while(*b!=-1){
        *c=(*b+jw)%10;
        jw=(*b+jw)/10;
        c++;
        b++;
    }
    while(jw){
        *c=jw%10;
        jw/=10;
        c++;
    }
    *c=-1;
}
void mul(int *a, int b, int *c){    //实现大数乘以一个较小的数,结果储存在*c指向的数组
    int jw=0;
    while(*a!=-1){
        *c=((*a)*b+jw)%10;
        jw=((*a)*b+jw)/10;
        a++;
        c++;
    }
    while(jw){
        *c=jw%10;

        c++;
        jw/=10;
    }
    *c=-1;
}
void prt(int *a){                //这是一个逆序输出数组的函数...非必要
    int cnt=0;
    for(;*a!=-1;a++)
        cnt++;
    a--;
    while(cnt--){
        printf("%d",*a);
        a--;
    }
    printf("\n");
}
void mcpy(int *a,int *b){    //这个实现了“a=b”,将一个大数从*b指向的数组中copy到*a指向的数组
    while(*b!=-1){
        *a=*b;
        a++;
        b++;
    }
    *a=-1;
}
int main(){
    int t0[110]={1,-1},t1[110]={1,-1},t2[110]={3,-1},t3[110];
    ll n,i;
    scanf("%lld",&n);
    if(n>=3){
        for(i=3;i<=n;i++){
            mul(t1,2,t0);    //用函数的话,个人感觉程序运行过程简洁直观,而且还容易调试BUG。
            plu(t2,t0,t3);   //如果把上面那些复制过来,可以想象这里会乱成什么样...
            mcpy(t1,t2);     //而且我也就不会有这么大的空间来加注释,说些废话了:)
            mcpy(t2,t3);
        }                    
        prt(t3);
    }
    else
        switch (n) {
            case 1:
                printf("1");
                break;
            case 2:
                printf("3");
                break;
        }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值