变态跳台阶
题目描述:
一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
设f[i] 表示当前跳到第 i 个台阶的方法数。那么f[n]就是所求答案。
f(n) = f(n - 1) + … + f(2) + f(1)。递归的终止条件为n 为 0的时候,我们返回1。
1.递归
class Solution:
def jumpFloorII(self, number):
if number == 0:
return 1
cnt = 0
for i in range(number):
cnt += self.jumpFloorII(i)
return cnt
2.数学
f(n)=f(n-1)+f(n-2)+…+f(1)
f(n-1)=f(n-2)+…f(1)
相减得:f(n)=2*f(n-1)
public class Solution {
public int JumpFloorII(int target) {
return 1<<(target-1);
//return (int)Math.pow(2,target-1);
}
}
跳台阶
题目描述
一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。
1.递归
class Solution {
public:
int jumpFloor(int number) {
//n级台阶,下台阶
//要么n-1,要么n-2
//f[n]=f[n-1]+f[n-2]
//初值f[0]=f[1]=1
if(number<=1) return 1;
return jumpFloor(number-1) + jumpFloor(number-2);
}
};
优点,代码简单好写,缺点:慢,会超时
时间复杂度:O(2^n)
空间复杂度:递归栈的空间
2.记忆化搜索
通过图会发现,方法一中,存在很多重复计算,因为为了改进,就把计算过的保存下来。
那么用什么保存呢?一般会想到map, 但是此处不用牛刀,此处用数组就好了。
class Solution {
public:
int jumpFloor(int n)
{
vector<int> dp(45, -1); // 因为答案都是>=0 的, 所以初始为-1,表示没计算过
return Fib(n, dp);
}
int Fib(int n, vector<int>& dp)
{
if(n<=1) return 1;
if(dp[n]!=-1) return dp[n];
return dp[n] = Fib(n-1, dp) + Fib(n-2, dp);
}
};
时间复杂度:O(n), 没有重复的计算
空间复杂度:O(n)和递归栈的空间
3.动态规划
如果想让空间继续优化,那就用动态规划,优化掉递归栈空间。
方法二是从上往下递归的然后再从下往上回溯的,最后回溯的时候来合并子树从而求得答案。
那么动态规划不同的是,不用递归的过程,直接从子树求得答案。过程是从下往上。
class Solution {
public:
int jumpFloor(int n)
{
vector<int> dp(n+1, 0);
dp[0] = dp[1] = 1;
for (int i=2; i<=n; ++i)
{
dp[i] = dp[i-1] + dp[i-2];
}
return dp[n];
}
};
时间复杂度:O(n)
空间复杂度:O(n)
继续优化
发现计算f[5]的时候只用到了f[4]和f[3], 没有用到f[2]…f[0],所以保存f[2]…f[0]是浪费了空间。
只需要用3个变量即可。
class Solution {
public:
int jumpFloor(int n)
{
if (n == 0 || n == 1) return 1;//return n也能调试通过?为啥
int a = 1, b = 1, c;
for (int i=2; i<=n; ++i) //只需要用3个变量
{
c = a + b;
a = b;
b = c;
}
return c;
}
};
AcWing 821. 跳台阶
题目描述:
一个楼梯共有n级台阶,每次可以走一级或者两级,问从第0级台阶走到第n级台阶一共有多少种方案。
输入格式
共一行,包含一个整数n。
输出格式
共一行,包含一个整数,表示方案数。
数据范围
1≤n≤15
样例
输入样例:
5
输出样例:
8
动态规划入门题
第0级台阶到第1级台 只有一种方法 上1级台阶
第0级台阶到第2级台 有两种方法 1种是0-2 上2级台阶 1种是上到1级台阶 再上2级台阶
第0级台阶到第3级台 有两种方法 1种是0-2 再2-3 1种是0-1 1-3 (其中0-1 1-2 2-3已经包含在前面的方法中了)
逆向来看就是 n台阶的方案数量 = n-1台阶方案数量 + n-2的方案数量
#include <iostream>
using namespace std;
int arr[20];
int main()
{
int n;
cin >> n;
arr[1] = 1; arr[2] = 2;
for(int i = 3;i <=15;i++){
arr[i] = arr[i-1]+arr[i-2];
}
cout << arr[n];
return 0;
}