[开心IT面试题] 爬楼梯问题

题目:

如果每次可以往上爬1个或2个或3个台阶,不能往下走,从第0个台阶开始,爬到第n个台阶有多少种方案?

 

举例:

当n = 1时,有1种方案。

当n = 2时,有2种方案

当n = 3时,有4种方案。

方案一:每次爬1个台阶,爬三次

方案二:第一次爬1个台阶,第二次爬2个台阶

方案三:第一次爬2个台阶,第二次爬1个台阶

方案四:爬一次,一次3个台阶

 

思路一:

设爬到第n个台阶总方案数为f(n)(n > 3).

1)当第一次只爬1个台阶时,剩下的台阶可爬方案数为f(n-1).
2)当第一次只爬2个台阶时,剩下的台阶可爬方案数为f(n-2).
3)当第一次只爬3个台阶时,剩下的台阶可爬方案数为f(n-3).

由1)和2)和3)可知,f(n) = f(n-1)+f(n-2)+f(n-3)

 

思路二:

设爬到第n-1个台阶时总方案中最后一步是1个台阶为A(n-1),2个台阶为B(n-1),3个台阶为C(n-1)。
设爬到第n个台阶总方案数为f(n)(n > 3).

当每往上爬多一个台阶,就有三种走法:

1)在之前所有方案基础上,再爬1个台阶,即A(n-1)+B(n-1)+C(n-1)种方案.
2)在之前所有方案中最后一次是爬1个台阶的方案基础上,变成爬2个台阶。即A(n-1)种方案.
3)在之前所有方案中最后一次是爬2个台阶的方案基础上,变成爬3个台阶。即B(n-1)种方案.

由1)和2)和3)可知,f(n) = 2A(n-1)+2B(n-1)+C(n-1)

 

代码:

递归法:

不足之处有两点:

一、long整型不够用,最多只能计算到n = 36;

二、时间复杂度为3^n;

//nSteps为总台阶数
long ClimbStairs(int nSteps)
{
    if(nSteps < 1)
    {
        return 0;
    }else if(nSteps == 1)
    {
        return 1;
    }else if(nSteps == 2)
    {
        return 2;
    }else if(nSteps == 3)
    {
        return 4;
    }else
    {
        return (ClimbStairs(nSteps - 1) + ClimbStairs(nSteps - 2) + ClimbStairs(nSteps - 3));
    }
}

 

优化之后代码:

解决递归法时间复杂度的不足。

//nSteps为总台阶数
long ClimbStairs(int nSteps)
{
    int nStep_1 = 1;    //nStep_1代表所有方案中最后一步是1个台阶的方案
    int nStep_2 = 0;    //nStep_2代表所有方案中最后一步是2个台阶的方案
    int nStep_3 = 0;    //nStep_3代表所有方案中最后一步是3个台阶的方案
    int sum = 1;        //sum代表解决方案种数,初始化的值是只有一个台阶的情况
    if(nSteps < 1)
    {
        return 0;
    }else if(nSteps == 1)
    {
        return sum;
    }else
    {
        //从最少有2个台阶开始计算
        for(int i = 2; i <= nSteps; i++)
        {
            nStep_3 = nStep_2;      //每加一个台阶,之前方案中的最后一步是2个台阶的变成爬3个台阶
            nStep_2 = nStep_1;      //每加一个台阶,之前方案中的最后一步是1个台阶的变成爬2个台阶
            nStep_1 = sum;          //每加一个台阶,之前方案中所有方案继续爬1个台阶
            sum = nStep_1+nStep_2+nStep_3;
        }
        return sum;
    }
}


使用大整数加法:

解决long整型不够用问题。

优化后代码:

//因为台阶多后,解决方案用整型类型不够用。
//所以使用大整数加法

#define BIT_TOTAL   500
typedef struct{
    char BigInt[BIT_TOTAL]; //BigInt[0]表示最高位
}BIGINT;

//将字符串反转
void Reverse(char nData[])
{
    char temp;
    for (int i = 0, j = strlen(nData)-1; (i != j) && (i - j != 1); i++, j--)
    {
        temp = nData[i];
        nData[i] = nData[j];
        nData[j] = temp;
    }
}

//大整数加法
BIGINT Add(BIGINT first, BIGINT second)
{
    BIGINT result;
    memset(&result, 0, sizeof(BIGINT));

    Reverse(first.BigInt);
    Reverse(second.BigInt);

    int temp = 0;
    int bit = 0;
    int length = strlen(first.BigInt) > strlen(second.BigInt) ? strlen(first.BigInt):strlen(second.BigInt);
    for (int i = 0; i < length; i++)
    {
        if(first.BigInt[i] == '\0')
        {
            first.BigInt[i] = '0';
        }
        if(second.BigInt[i] == '\0')
        {
            second.BigInt[i] = '0';
        }
        temp = (first.BigInt[i]-'0') + (second.BigInt[i]-'0') + bit;
        if (temp >= 10)
        {//有进位
            temp = temp % 10;
            bit = 1;
        }else
        {//无进位
            bit = 0;
        }
        result.BigInt[strlen(result.BigInt)] = char(temp + '0');
    }
    //判断是否要在最高位加一位数
    if (bit == 1)
    {
        result.BigInt[strlen(result.BigInt)] = char(bit + '0');
    }
    Reverse(result.BigInt);
    return result;
}

//nSteps为总台阶数
BIGINT ClimbStairs(int nSteps)
{
    BIGINT nStep_1;             //nStep_1代表所有方案中最后一步是1个台阶的方案
    BIGINT nStep_2;             //nStep_2代表所有方案中最后一步是2个台阶的方案
    BIGINT nStep_3;             //nStep_3代表所有方案中最后一步是3个台阶的方案
    BIGINT allSolution;
    memset(&nStep_1, 0, sizeof(BIGINT));
    memset(&nStep_2, 0, sizeof(BIGINT));
    memset(&nStep_3, 0, sizeof(BIGINT));
    memset(&allSolution, 0, sizeof(BIGINT));

    nStep_1.BigInt[0] = '1';    //只有一个台阶时的解决方案

    if(nSteps < 1)
    {
        allSolution.BigInt[0] = '0';
        return allSolution;
    }else if(nSteps == 1)
    {
        allSolution.BigInt[0] = '1';
        return allSolution;
    }else
    {
        allSolution.BigInt[0] = '1';
        //从最少有2个台阶开始计算
        for(int i = 2; i <= nSteps; i++)
        {
            //每加一个台阶,之前方案中的最后一步是2个台阶的变成爬3个台阶
            memcpy(&nStep_3, &nStep_2, sizeof(BIGINT));
            //每加一个台阶,之前方案中的最后一步是1个台阶的变成爬2个台阶
            memcpy(&nStep_2, &nStep_1, sizeof(BIGINT));
            //每加一个台阶,之前方案中所有方案继续爬1个台阶
            memcpy(&nStep_1, &allSolution, sizeof(BIGINT));

            memcpy(&allSolution, &Add(nStep_1, nStep_2), sizeof(BIGINT));
            memcpy(&allSolution, &Add(allSolution, nStep_3), sizeof(BIGINT));
        }
        return allSolution;
    }
}

 

 

扩展题:
1、如果每次可以往上爬1个或2个或3个台阶,爬楼梯过程中允许最多往下走一次,每次走1个台阶,从第0个台阶开始,爬到第n个台阶有多少种方案?

答案:爬楼梯问题升级版

 

2、如果每次可以往上爬1个或2个或3个台阶,爬楼梯过程中允许最多往下走k次,每次走1个台阶,从第0个台阶开始,爬到第n个台阶有多少种方案?

答案:爬楼梯问题豪华版



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值