参考链接:https://time.geekbang.org/column/article/41440
基本的走台阶问题
问题描述:假设有n个台阶,每跨一步只能上1阶或者2阶台阶。求总共有多少种走法
思路1:假设n阶台阶有f(n)种走法,可以通过递归的方式进行求解。因为一步可以上1阶或者2阶台阶,那如果先上1个台阶则只需求出剩下的n-1个台阶的走法,如果先上2个台阶则只需求出剩下的n-2个台阶的走法,所以求n个台阶的走法变为求n-1和n-2个台阶的走法,则有f(n) = f(n - 1) + f(n - 2)(其中n > 2)。
基于以上思路,代码实现如下:
/*
* @brief 利用递归解决该问题
*
* @method: SolveStep
* @access: public
* @param: size_t nStair
* @Return: size_t
* @author: RF_LYF
* @since: 2019/4/23 9:57
*/
DWORD SolveStep(size_t nStair)
{
if(nStair <= 0)
return 0;
else if(nStair < 3 && nStair > 0)
return nStair;
else
return SolveStep(nStair - 1) + SolveStep(nStair -2);
}
该方法,当台阶的个数超过一定值得时候,求解速度很慢。
思路2:上面方法求解速度慢主要原因是存在好多重复计算。如下图所示:
由图可知在求解f(6)时f(3)被计算了3次,f(4)被被计算了2次,这种重复计算在上面的实现方式中出现很多,因此我们为了避免重复计算,可以将已经计算过的结果保存下来,从而减少计算次数,提高计算效率 。
基于以上分析,代码实现如下:
/**
* @brief 采用自顶向下的动态规划方式进行求解
*
* @method: SolveStepOpti
* @access: public
* @param: size_t nStair
* @param: int * Counter
* @Return: ULONG
* @author: RF_LYF
* @since: 2019/4/23 10:33
*/
DWORD SolveStepOpti(size_t nStair, int *Counter)
{
if(0 !=Counter[nStair])
return Counter[nStair];
if(nStair <= 0)
return 0;
else if(nStair > 0 && nStair < 3)
{
return Counter[nStair] = nStair;
}
else
{
Counter[nStair] = SolveStepOpti(nStair - 1, Counter) + SolveStepOpti(nStair - 2, Counter);
return Counter[nStair];
}
}
该方法的计算速度有了明显的提升。
思路3:利用循环的方式,完成该问题,采用自底向上的动态规划
/**
* @brief 利用循环求解问题,自底向上的动态规划求解问题
*
* @method: SolveStepOpti2
* @access: public
* @param: size_t nStair
* @Return: DWORD
* @author: RF_LYF
* @since: 2019/4/23 10:52
*/
DWORD SolveStepOpti2(size_t nStair)
{
if(nStair <= 0)
return 0;
else
{
DWORD step = 0;
DWORD fstep = 1;
DWORD ffstep = 0;
for(size_t i = 1; i <= nStair; ++i)
{
step = fstep + ffstep;
ffstep = fstep;
fstep = step;
}
return step;
}
}
该方法的计算速度也很快。
变形的走台阶问题
问题描述:还是n个台阶,同样每次可以跨1个台阶或者2个台阶。假定第一次迈的是左脚,求走完第n个台阶迈的正好是左脚(或者右脚)总共有多少走法
思路1:问题可以简化为,求n个台阶奇数次(或者偶数次)走完,共有多少种走法。假设将n个台阶奇数次走完共有 f奇(n)次,则
f奇(n) = f偶(n-1) + f偶(n-2),因为从n到n-1或者n-2已经走了一步。
基于以上分析,通过递归的方式实现,代码如下:
/**
* @brief 递归法实现
*
* @method: SolveStepOddEven
* @access: public
* @param: int nStair
* @param: size_t nStep(用来记录步数)
* @param: DWORD& nCountOdd(用来记录奇数次走法的数目)
* @param: DWORD& nCountEven(用来记录偶数次走法的数目)
* @Return: void
* @author: RF_LYF
* @since: 2019/4/23 11:28
*/
void SolveStepOddEven(int nStair, size_t nStep, DWORD& nCountOdd, DWORD& nCountEven)
{
if(nStair < 0)
return;
else if(nStair == 0)
{
if(nStep % 2 == 1)
++nCountOdd;
else
++nCountEven;
return;
}
else
{
++nStep;
SolveStepOddEven(nStair - 1, nStep, nCountOdd, nCountEven);
SolveStepOddEven(nStair - 2, nStep, nCountOdd, nCountEven);
}
}
思路2:利用自底向上动态规划方式实现
/**
* @brief 动态规划 + 循环方法
*
* @method: SolveStepOddOpti
* @access: public
* @param: int nStair
* @param: DWORD& nCountOdd(用来记录奇数次走法的数目)
* @param: DWORD& nCountEven(用来记录偶数次走法的数目)
* @Return: void
* @author: RF_LYF
* @since: 2019/4/23 14:05
*/
void SolveStepOddEvenOpti(int nStair, DWORD& nCountOdd, DWORD& nCountEven)
{
if(nStair <= 0)
return;
else if(nStair == 1)
{
nCountOdd = 1;
nCountEven = 1;
}
else
{
DWORD *pFOdd = new DWORD[nStair + 1];
DWORD *pFEven = new DWORD[nStair + 1];
pFOdd[0] = 0,pFOdd[1] = 1;
pFEven[0] = 1,pFEven[1] = 0;
for(int i = 2; i <= nStair; ++i)
{
pFOdd[i] = pFEven[i - 1] + pFEven[i - 2];
pFEven[i] = pFOdd[i - 1] + pFOdd[i - 2];
}
nCountOdd = pFOdd[nStair];
nCountEven = pFEven[nStair];
delete []pFOdd;
delete []pFEven;
}
}
升级的台阶问题
问题描述:一个台阶总共有n级,如果每次可以跳1阶,2阶……n阶,求总共有多少种跳法
思路:
(1)n = 1时 f(1) = 1
(2)n = 2时分为2种方式,第1种跳1阶剩f(1)种跳法,第2种跳2阶只有1种跳法,因此 f(2) = f(2 - 1) + f(2 -2) = f(1) + 1 (3)当n=3时分为3种方式,先跳1阶剩f(2)种跳法,先跳2阶剩f(1)种跳法,先跳3阶只有1种跳法,因此 f(3) = f(2) + f(1) + 1 (4)当n个台阶时,则满足f(n) = f(n-1)+ f(n-2) + f(n-3) + ……+ f(1) + 1
(5)当n-1个台阶时,f(n-1) = f(n-2) + f(n-3) + ……+ 1
(6)f(n) - f(n-1) = f(n-1),则有f(n) = 2f(n-1),该公式则为递推公式
基于以上分析,代码实现如下:
long long SolveStepUpdate(int nStair)
{
if(nStair <= 0)
return 0;
else if(nStair == 1)
return 1;
else
{
long long step = 0;
long long fstep = 1;
for(int i = 2; i <= nStair; ++i)
{
step = 2 * fstep;
fstep = step;
}
return step;
}
}