力扣之青蛙跳台阶

目录

青蛙跳台阶

 暴力递归

 带备忘录的递归解法(自顶向下)

 动态规划(自低向上)


青蛙跳台阶

问题:一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶。求该青蛙跳上一个 10 级的台阶总共有多少种跳法?

 暴力递归

时间复杂度: 解决一个子问题的时间[f(n-1)+f(n-2)]*子问题个数[递归树结点个数]

                    即 O(1)*O(2^N) = O(2^N)

int recursion(int n)
{
    if(n==1)
        return 1;
    if(n==2)
        return 2;
    return recursion(n-1)+recursion(n-2);
}

优点:简单

缺点:时间复杂度高

 带备忘录的递归解法(自顶向下)

备忘录:一个数组或者一个哈希map

时间复杂度:0(n)

int memoRec(int n)
{
    if(n<=1)
        return 1;
    if(n==2)
        return 2;
    // map<int,int> m;
    unordered_map<int,int> m;
    unordered_map<int,int>::iterator it;
    m.insert(make_pair(0,1));
    m[1]=2;
    m.insert(make_pair(2,2));
    for(auto it=m.begin();it!=m.end();++it){
        if(m.count(n)){
            return it->second;
        }else{
            m.insert(make_pair(n,(memoRec(n-1)+memoRec(n-2))));
            return it->second;
        }
    }
    return 0;
}

图解哈希表及其原理:图解哈希表及其原理 - Er_HU - 博客园 (cnblogs.com)

(unordered_map哈希表):

http://t.csdn.cn/vAhWt

http://t.csdn.cn/TalTP

哈希表的创建:http://t.csdn.cn/8CE4b

 动态规划(自低向上)

典型特征:最优子结构、状态转移方程、边界、重叠子问题

在青蛙跳阶问题中:

f(n-1)和f(n-2) 称为 f(n) 的最优子结构

f(n)= f(n-1)+f(n-2)就称为状态转移方程

f(1) = 1, f(2) = 2 就是边界值

比如f(10)= f(9)+f(8),f(9) = f(8) + f(7) ,f(8)就是重叠子问题。

而这个状态迁移方程类似 斐波那契数列

动态规划的解题套路:

什么样的问题可以考虑使用动态规划解决呢?

★ 如果一个问题,可以把所有可能的答案穷举出来,并且穷举出来后,发现存在重叠子问题,就可以考虑使用动态规划。

比如一些求最值的场景,如最长递增子序列、最小编辑距离、背包问题、凑零钱问题等等,都是动态规划的经典应用场景。

动态规划的解题思路:

动态规划的核心思想就是拆分子问题,记住过往,减少重复计算。 并且动态规划一般都是自底向上的

因此到这里,基于青蛙跳阶问题,我总结了一下我做动态规划的思路:

1、穷举分析

2、确定边界

3、找出规律,确定最优子结构

4、写出状态转移方程

int dynamicPlan(int n)
{
    if(n <= 1)  return 1;//0 || 1个台阶
    if(n == 2)  return 2;//2 个台阶
    int fn=0,fn_1=1,fn_2=2;//fn_2保存f(n-2),fn_1保存f(n-1),fn保存f(n)  //f(3)=f(1)+f(2);
    for(int i = 3; i <= n; ++i)//i:台阶阶数
    {
        fn = (fn_1 + fn_2) % 1000000007;//第i个台阶跳法:fn_2:i-2个台阶跳法,fn_1:i-1个台阶跳法
        fn_1 = fn_2;//fn_1保存fn_2,类似f(n-1)保存f(n-2),因为f(n-1)=f(n-2)+f(n-3),相当于一层一层保存上来
        fn_2 = fn;//fn_2保存fn,类似f(n-2)保存f(n),因为上一步操作中f(n-2)备份在了f(n-1)里面,现在f(n-2)就没用了,所有可以用来保存f(n),层层保存,类似临时变量
    }
    return fn;
}

动态规划优化版:http://t.csdn.cn/0NdLj

参考文献:看一遍就理解:动态规划详解 - 知乎 (zhihu.com)

 全部源码:

#include <iostream>
#include <map>
using namespace std;
/*
问题:一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶。
求该青蛙跳上一个 10 级的台阶总共有多少种跳法?
*/
/*
暴力递归
时间复杂度:解决一个子问题的时间[f(n-1)+f(n-2)]*子问题个数[递归树结点个数]
            即 O(1)*O(2^N) = O(2^N)
*/
int recursion(int n)
{
    if(n==1)
        return 1;
    if(n==2)
        return 2;
    return recursion(n-1)+recursion(n-2);
}
/*
带备忘录的递归解法(自顶向下)
备忘录:一个数组或者一个哈希map
时间复杂度:0(n)
*/
// int memoRec(int n)
// {

// }
/*
动态规划(自低向上)典型特征:最优子结构、状态转移方程、边界、重叠子问题。
在青蛙跳阶问题中:
f(n-1)和f(n-2) 称为 f(n) 的最优子结构
f(n)= f(n-1)+f(n-2)就称为状态转移方程
f(1) = 1, f(2) = 2 就是边界值
比如f(10)= f(9)+f(8),f(9) = f(8) + f(7) ,f(8)就是重叠子问题。
而这个状态迁移方程类似 斐波那契数列
动态规划的解题套路:
什么样的问题可以考虑使用动态规划解决呢?
★ 如果一个问题,可以把所有可能的答案穷举出来,并且穷举出来后,发现存在重叠子问题,就可以考虑使用动态规划。
比如一些求最值的场景,如最长递增子序列、最小编辑距离、背包问题、凑零钱问题等等,都是动态规划的经典应用场景。
动态规划的解题思路:
动态规划的核心思想就是拆分子问题,记住过往,减少重复计算。 并且动态规划一般都是自底向上的
因此到这里,基于青蛙跳阶问题,我总结了一下我做动态规划的思路:
1、穷举分析
2、确定边界
3、找出规律,确定最优子结构
4、写出状态转移方程

在斐波那契数列中:f(0) = 0, f(1)=1, f(2)=1;
fn = f(n-1)+f(n-2);
*/
int dynamicPlan(int n)
{
    if(n <= 1)  return 1;//0 || 1个台阶
    if(n == 2)  return 2;//2 个台阶
    int fn=0,fn_1=1,fn_2=2;//fn_2保存f(n-2),fn_1保存f(n-1),fn保存f(n)  //f(3)=f(1)+f(2);
    for(int i = 3; i <= n; ++i)//i:台阶阶数
    {
        fn = (fn_1 + fn_2) % 1000000007;//第i个台阶跳法:fn_2:i-2个台阶跳法,fn_1:i-1个台阶跳法
        fn_1 = fn_2;//fn_1保存fn_2,类似f(n-1)保存f(n-2),因为f(n-1)=f(n-2)+f(n-3),相当于一层一层保存上来
        fn_2 = fn;//fn_2保存fn,类似f(n-2)保存f(n),因为上一步操作中f(n-2)备份在了f(n-1)里面,现在f(n-2)就没用了,所有可以用来保存f(n),层层保存,类似临时变量
    }
    return fn;
}
int main()
{
    int sum1 = recursion(45);
    cout<<"暴力递归结果:"<<sum1<<endl;
    // int sum2 = memoRec(45);//44个台阶前
    // cout<<"备忘录递归:"<<sum2<<endl;
    int sum3 = dynamicPlan(45);
    cout<<"动态规划之斐波拉契:"<<sum3<<endl;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值