动态规划2:台阶问题

博客介绍了如何使用动态规划解决上台阶问题,分析了问题并提出使用递归或记忆化搜索来降低时间复杂度,强调了避免溢出的处理方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目:有n级台阶,一个人每次上一级或者两级,问有多少种走完n级台阶的方法。为了防止溢出,请将结果Mod 1000000007给定一个正整数int n,请返回一个数,代表上楼的方式数。保证n小于等于100000。

测试样例:1 返回:1

思路:这是一个实际问题,实际问题大多使用动态规划来解决。先分析问题:按照动态规划的套路,这个问题其实可以使用建立一个二维数组的方式来解决,即假设每次可以向上走的步数的元素是数组penny[2,3,4],需要走的台阶数目是n=aim,那么就等同于上面的零钱凑整问题,建立二维数组dp[i][j]表示使用penny[0~i]的元素来走到j级台阶的方案数目,先求出第1行是用penny[0]走完j级台阶,于是是0或者1,再计算第1列,即使用penny[0~i]来走完0级台阶,于是方案数是1,之后从上到下,从左网友进行计算,直到最后求出dp[n-1][aim]即为所求结果。

本题由于上台阶的可能只有1级或者2级这2种,因此是一个退化的动态规划问题,可以使用更加简单灵活的方式来解决问题。

本题可以直接使用递归来解决问题:

import java.util.*;
//台阶问题:递归解决
public class GoUpstairs {
	public static void main(String[] args) {
		System.out.println(new GoUpstairs().countWays(50));
	}
    public int countWays(int n) {
    	 //特殊输入
        if(n<=0) return 0;
        //边界条件
        if(n==1) return 1;
        if(n==2) return 2;
        return countWays(n-1)%1000000007+countWays(n-2)%1000000007;
    }
}

但是这属于暴力递归搜索算法,时间复杂度巨大,测试时当n=50就需要几分钟来计算,因此显然是不行的,为了降低时间复杂度,关键是减少重复计算,因此应该使用动态规划的方法,所谓动态规划方法就是将已经计算过的值保留下来以便下次计算需要用到时可以复用(思想等同于记忆搜索算法),因此实现这种将已经计算过的值保留以便复用的思策略有2种:

策略1:将每次中间计算得到的结果使用哈希表(map,一维数组,二维数组)进行保存,在使用时先取哈希表中寻找,找到直接利用找不到时再计算。-------记忆搜索算法

策略2:规定计算路径,总是将基础值在前面进行计算,后面的计算可以完全依赖于前面的计算结果得到,即按照这种计算路径时,在计算后面的结果时恰好前面的结果已经计算出来了,并且可以直接使用前面的计算结果。常见的计算路径有:一维数组从左到右;二维数组从上到下从左到右;斐波那契数列,台阶数列等从前到后---------动态规划算法

 

本题中根据直接观察的规律或者暴力搜索得到的规律f(n)=f(n-1)+f(n-2)可知,一个结果完全依赖于前面的2个计算结果,因此按照从前往后的计算路径,每次计算时使用前面的计算结果,同时将当前计算出来的结果保存到一个全局的数组中,就可以解决问题。当然这个问题很简单,可以不使用数组而仅仅使用一个全局的变量count来表示当前n的计算结果即可(因为本题是线性的,一个计算结果只会被使用一次,因此可以被覆盖,如果有可能被再次使用那么将其放入到一个数组中进行保留,即数组中可以保留每一个计算结果,不会被后面的计算结果覆盖,于是可以多次重复使用,这要根据具体的问题具体决定是使用二维数组、一维数组、还是一个变量)。

import java.util.*;
//台阶问题:动态规划
public class GoUpstairs {
    public int countWays(int n) {
        //特殊输入
        if(n<=0) return 0;
        
//全局变量用来计算当前n的计算结果,由于n计算结果只会被复用1次,所以可以被覆盖,否则使用数组
        int n1=1;
        int n2=2;
        //从前往后计算并保留结果
        if(n==1) return 1;
        if(n==2) return 2;
        
        for(int i=3;i<=n;i++){
            int temp=n1%1000000007+n2%1000000007;
            n1=n2%1000000007;
            n2=temp%1000000007;
        }
        return n2;
    }
}

常识:为了避免溢出,常常对结果取余处理,可以像上面这样对每个可能发生溢出的变量进行取余,也可以针对性地只对可能发生溢出的变量作溢出处理,这里实际上溢出总是发生在2个n相加的时候,因此只要对temp进行取余即可,只要temp不溢出,显然n1,n2也不会溢出:

inttemp=(n1+n2)%1000000007;

n1=n2;

n2=temp;


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值