题目:
如果每次可以往上爬1个或2个或3个台阶,爬楼梯过程中允许最多往下走k次,每次走1个台阶,从第0个台阶开始,爬到第n个台阶有多少种方案?
举例:
当n = 2,k = 2时,有12种方案
方案1:走1,走1
方案2:走1,走1,退1,走1
方案3:走1,走1,退1,走1,退1,走1
方案4:走2
方案5:走2,退1,走1
方案6:走2,退1,走1,退1,走1
方案7:走1,退1,走1,走1
方案8:走1,退1,走1,走1,退1,走1
方案9:走1,退1,走2
方案10:走1,退1,走2,退1,走1
方案11:走1,退1,走1,退1,走1,走1
方案12:走1,退1,走1,退1,走2
思路:
所有解决方案可分为以下几种情况:
1、没有往下走过
2、已经往下走过1次
3、已经往下走过2次
…、……
k-1、已经往下走过(k-1)次
k、已经往下走过k次
用Solution[k+1][3]二维数组表示上面所有方案。
第一维表示该方案中已经往下走过多少次。
第二维表示该方案中最后一步是多少个台阶。
当每多一个台阶时,根据已经往下走过多少次分情况处理:假设已经往下走过m次
1)所有方案基础上,再爬1个台阶。
2)最后一次是爬1个台阶的方案,变成最后一步爬2个台阶。
3)最后一次是爬2个台阶的方案,变成最后一步爬3个台阶。
4)所有方案基础上,可循环操作“爬1个台阶,然后往下走1个台阶”,再爬1个台阶。(最少循环0次,最多循环(k-m)次)
5)最后一次是爬1个台阶的方案,变成最后一步爬2个台阶,可循环操作“往下走1个台阶,再爬1个台阶”(最少循环0次,最多循环(k-m)次)
6)最后一次是爬2个台阶的方案,变成最后一步爬3个台阶,可循环操作“往下走1个台阶,再爬1个台阶”(最少循环0次,最多循环(k-m)次)
代码:
#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为总台阶数,k为爬楼梯过程中可以往下走的次数
BIGINT ClimbStairs3(int nSteps, int k)
{
BIGINT allSolution;
memset(&allSolution, 0, sizeof(BIGINT));
if(nSteps < 1)
{
allSolution.BigInt[0] = '0';
return allSolution;
}else if(nSteps == 1)
{
allSolution.BigInt[0] = char(k+'1');
return allSolution;
}else
{
//nStep[x][y]代表所有方案,
//x表示该方案中已经往下走过x次
//y表示该方案中最后一步是(y+1)个台阶
//如nStep[4][1]表示该方案已经往下走过4次,最后一步是2个台阶
BIGINT (*nStep)[3];
BIGINT (*tempStep)[3];
nStep = new BIGINT[k+1][3];
tempStep = new BIGINT[k+1][3];
memset(nStep, 0, 3*(k+1)*sizeof(BIGINT));
memset(tempStep, 0, 3*(k+1)*sizeof(BIGINT));
//只有一个台阶时的解决方案
for(int i = 0; i <= k; i++)
{
nStep[i][0].BigInt[0] = '1';
}
//从最少有2个台阶开始计算
for(int i = 2; i <= nSteps; i++)
{
for(int x = 0; x <= k; x++)
{
//在所有方案中最后一步是2个台阶的变成3个台阶
memcpy(&tempStep[x][2], &nStep[x][1], sizeof(BIGINT));
//在所有方案中最后一步是1个台阶的变成2个台阶
memcpy(&tempStep[x][1], &nStep[x][0], sizeof(BIGINT));
//在所有方案中,继续再走1个台阶
memcpy(&tempStep[x][0], &Add(tempStep[x][0], nStep[x][0]), sizeof(BIGINT));
memcpy(&tempStep[x][0], &Add(tempStep[x][0], nStep[x][1]), sizeof(BIGINT));
memcpy(&tempStep[x][0], &Add(tempStep[x][0], nStep[x][2]), sizeof(BIGINT));
for(int y = x+1; y <= k; y++)
{
//在所有方案中,循环(走一步,退一步),再走一步
memcpy(&tempStep[y][0], &Add(tempStep[y][0], nStep[x][0]), sizeof(BIGINT));
memcpy(&tempStep[y][0], &Add(tempStep[y][0], nStep[x][1]), sizeof(BIGINT));
memcpy(&tempStep[y][0], &Add(tempStep[y][0], nStep[x][2]), sizeof(BIGINT));
//在所有方案中最后一步是1个台阶的变成2个台阶,循环(退一步,再走一步)
memcpy(&tempStep[y][0], &Add(tempStep[y][0], nStep[x][0]), sizeof(BIGINT));
//在所有方案中最后一步是2个台阶的变成3个台阶,循环(退一步,再走一步)
memcpy(&tempStep[y][0], &Add(tempStep[y][0], nStep[x][1]), sizeof(BIGINT));
}
}
memcpy(nStep, tempStep, 3*(k+1)*sizeof(BIGINT));
memset(tempStep, 0, 3*(k+1)*sizeof(BIGINT));
}
for(int i = 0; i <= k; i++)
{
memcpy(&allSolution, &Add(allSolution, nStep[i][0]), sizeof(BIGINT));
memcpy(&allSolution, &Add(allSolution, nStep[i][1]), sizeof(BIGINT));
memcpy(&allSolution, &Add(allSolution, nStep[i][2]), sizeof(BIGINT));
}
delete[] tempStep;
delete[] nStep;
return allSolution;
}
}
原题目1:如果每次可以往上爬1个或2个或3个台阶,不能往下走,从第0个台阶开始,爬到第n个台阶有多少种方案?
答案:爬楼梯问题
原题目2:如果每次可以往上爬1个或2个或3个台阶,爬楼梯过程中允许最多往下走一次,每次走1个台阶,从第0个台阶开始,爬到第n个台阶有多少种方案?
答案:爬楼梯问题升级版