动态规划入门青蛙跳台阶问题

0 题目

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

答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。

1 解题分析

动态规划的组成部分:

  • (1)确定状态
  • 划归为最后一步看问题。
  • 划归为子问题

本题中以青蛙跳台阶的最后一步来看:青蛙跳上n级台阶有两种情况,要么青蛙跳一步,要么青蛙跳两步。

1)当为1时即青蛙跳一步的时候:此时青蛙前面已经跳了n-1个台阶,有f(n-1)种跳法。

2)当为2时即青蛙跳二步的时候:此时青蛙前面已经跳了n-2个台阶,有f(n-2)种跳法。
 

  • (2)转移方程

由于青蛙跳最后一步是确定的状态,那么他跳n个台阶的时候,取决于前n-1个台阶的跳法及前n-2个台阶的跳法

因此青蛙跳n个台阶的跳法有f(n)=f(n-1)+f(n-2)种跳法。转移方程即为:f(n)=f(n-1)+f(n-2)

  • (3) 确定初始条件及边界情况

当台阶为0时, 即f(0)=1(题中规定)

当台阶为1时,青蛙只有一种跳法,f(1)=1

当台阶为2时青蛙有2种跳法,f(2)=2

  • (4)确定计算顺序

f(0),f(1),f(2)。。。。。。。

综上动态规划算法步骤如下:

(1)定义dp为一维数组,其中dp[i]值代表青蛙第i个台阶时,所需要的步数

(2)dp[i+1] =dp[i]+dp[i-1]

(3)初始状态:dp[0]=0,dp[1]=1,即初始化前两个数字

(4)返回值dp[n]

代码如下:

class Solution {
    public int fib(int n) {
        if(n == 0) return 1;
        //开一个dp数组,数组长度n+1
        int[] dp = new int[n + 1];
       //dp初始化
        dp[0] = 1;
        dp[1] = 1;
       //从第二个开始循环迭代
        for(int i = 2; i <= n; i++){
            //状态转移方程
            dp[i] = dp[i-1] + dp[i-2];
            dp[i] %= 1000000007;//防止int类型溢出
        }
        return dp[n];
    }
}

 求余运算规则: 设正整数 x, y, px,y,p ,求余符号为⊙ ,则有(x+y)⊙p=(x⊙p+y⊙p)⊙p 。
解析: 根据以上规则,可推出f(n)⊙p=[f(n−1)⊙p+f(n−2)⊙p]⊙p。从而可以在循环过程中每次计算sum=(a+b)⊙1000000007 ,此操作与最终返回前取余等价。

取余原因

“int32类型是十位数,对1e9取模可防止int32溢出”、“1e9+7是质数,对质数取模可以尽可能地让模数避免相等”以及“1e9+7是离1e9最近的质数,比较好记”

节约空间的迭代:
在动态规划过程中发现,每次用到的仅仅是前两次的值。因此可以用两个变量保存每次计算后的值,并更新他。

 public int numWays(int n) {
       if(n==1 ||n==0){
           return 1;
       }
       //定义a,b来保存每次计算后的结果,a为小数,b为大数据
        int a = 1;
        int b = 1;
       //c来保存a+b的结果
        int c = 0;
        for(int i =2; i<=n;i++){
            c =(a +b)%1000000007;
            a = b ;
            b = c ;
        }
         
          return  c;
    }
    

从计算效率、空间复杂度上看,节约空间的迭代是本题的最佳解法。

【2】有一楼梯共m级,刚开始时你在第一级,若每次只能跨上一级或者二级,要走上m级,共有多少走法?注:规定从一级到一级有0种走法。
给定一个正整数int n,请返回一个数,代表上楼的方式数。保证n小于等于100。为了防止溢出,请返回结果Mod 1000000007的值。

JD笔试题,这里从1级到1级算0种。且初始你在第一级。

public class GoUpstairs {
    public int countWays(int n) {
        // write code here
        int[] dp=new int[n+1];
        dp[0]=0;
        dp[1]=0;
        dp[2]=1;
        dp[3]=2;
        for(int i=4;i<n+1;i++){
            dp[i]=(dp[i-1]+dp[i-2])%1000000007;
        }
        return dp[n];
    }

 注意这里与青蛙跳台阶略有不同的是边界及初始值的确定不同,其他都一样。

【3】变态跳台阶

题目描述

一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。

考察点:数学建模能力,公式推导及规律总结。

 动态规划

我们设跳到一级有F(1)种方法,跳到二级有F(2)种方法.....F(3)...F(4).....

由题意,我们从每一级台阶都能跳到第n级,所以

F(n)=F(n-1)+F(n-2)+F(n-3).......F(1)

而 F(n-1)=F(n-2)+F(n-3)+F(n-4).......F(1)

相减得F(n)-F(n-1)=F(n-1)   ====>   F(n)=2*F(n-1);(数学归纳法)

于是只要保留1项结果就可以了,再分析初始值,

f(1)=1;

f(2)=2;……

于是f(n)=2^(n-1);

class Solution {
public:
    int jumpFloorII(int number) {
        vector<int> result(number + 1);
        result[0] = 1;
        result[1] = 1;
        result[2] = 2;
        for (int i = 3; i <= number; i++)
            result[i] = 2 * result[i - 1];//动态规划的条件方程
        return result[number];//包含了以上的情况
    }
};

 由递推关系可知 F(n) = 2 * F(n-1),用一个变量来记录每次计算产生的F(n-1)的值,直到循环到n。

  public int frog(int target){
        if(target==0||target==1)
            return 1;
        if(target==2)
            return 2;
        int sum=1;
        for(int i=1;i<target;i++){
            sum *= 2;
        }
        return sum;
    }

常识:一个整数除以2可以使用向右位移1位来实现,即2>>1=1;一个整数乘以2可以使用向左位移1位来实现,4<<1=8,于是本题中f(n)=1<<(n-1),注意左移动-1并不等价于右移1,于是对n=0,n=1特殊考虑。

publicclass Solution {
    public int JumpFloorII(int target) {
       //特殊输入:对于target为0的情况OJ并不关心
        if(target<0) return 0;
        if(target==1) return 1;
       return 1<<(target-1);
    }
}

2 小结

解题套路:

  • (1)确定状态:划归为最后一步
  • (2)确定转移方程
  • (3)确定初始状态及边界
  • (4)确定计算顺序

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值