走台阶问题(递归方法+动态规划)

参考链接: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;
	}
}

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值