题目:
如果每次可以往上爬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个台阶有多少种方案?
答案:爬楼梯问题豪华版