走楼梯问题 题解
走楼梯问题
题目:
有一座高度是10级台阶的楼梯,从下往上走,每跨一步只能向上1级或者2级台阶。要求用程序来求出一共有多少种走法。
比如,每次走1级台阶,一共走10步,这是其中一种走法。我们可以简写成 1,1,1,1,1,1,1,1,1,1。
再比如,每次走2级台阶,一共走5步,这是另一种走法。我们可以简写成 2,2,2,2,2。
当然,除此之外,还有很多很多种走法。
如何求解这个问题?
排列组合思想写多层的嵌套循环遍历所有可能的解。每遍历一次,计数器加一。
时间复杂度是指数级。
long long ans=0; void dfs(int n) { if (n==0) ans++; else { if (n-1>=0)dfs(n-1); if (n-2>=0)dfs(n-2); } }
递归调用图示
如何求解这个问题?
动态规划思想(大事化小,小事化了)
试想一下假设你只差最后一步走到第10个台阶,这种会有几种情况?
两种。
一种是从第9阶一步走一级台阶上来。
另一种是从第8阶一步走两级台阶上来。
现在假设从第0阶走到第9阶有x种走法,从第0阶走到第8阶有y种走法,问:从第0阶走到第10阶有几种走法?
x+y
为表达方便设f(m)表示从第0阶到第m阶的走法,那么f(10)=f(9)+f(8)。如果计算f(9)和f(8)。
只有1个台阶或2个台阶有多少种走法?
f(1)=1
f(2)=2;
f(n)=f(n-1)+f(n-2) (n>=3)
动态规划三个基本概念
最优子结构
边界
状态转移公式
f(9),f(8)就是f(10)的最优子结构。
f(1),f(2)就是边界。
f(n)=f(n-1)+f(n-2) 就是状态转移方程。
如何写代码?
方法一:递归求解O(2n)
long long fib(int n) { if (n<=2) return n; else return fib(n-1)+fib(n-2); }
从下图中发现递归求解过程中存在大量重复计算。
如何避免大量的重复计算?我们可以开一个数组来保存计算的结果,下次进行计算前先看一个这个过程是否已经计算过,计算过,直接从数组中取出来。
方法二:记忆化O(n)
memset(F,-1,sizeof(F)); long long fib(int n) { if (F[n]!=-1) return F[n]; if (n<=2) return F[n]=n; else return F[n]=fib(n-1)+fib(n-2); }
这是动态规划的方法。
刚才我们是从所有求的结果向已知的方向递归求解,现在可以从开始向结果递推求解。
方法三:递推求解O(n)
long long fib(int n) { F[1]=1; F[2]=2; for (int i=3;i<=n;i++) { F[n]=F[n-1]+F[n-2] } return F[n]; }