三步问题(输入较大数据)-----从递归到动态规划的最优解四步法

01.问题描述

三步问题。有个小孩正在上楼梯,楼梯有n阶台阶,小孩一次可以上1阶、2阶或3阶。实现一种方法,计算小孩有多少种上楼梯的方式。结果可能很大,你需要对结果模1000000007。n范围在[1, 1000000]之间。

函数调用:int waysToStep(int n)

02.问题分析

首先我们可以看到这道题的数据很大,这个对编程者也是很大的一个挑战。一般这种题会要求对结果取模。除开很大的数据,我们很多人看到的第一个想法应该是递归吧。只要想着初始条件
n = 1, return 1;
n = 2, return 2;
n = 3, return 4;
那么递归的条件我们就能很容易找到。

(1)递归方法

这种方法的时间复杂度是真的很大很大,呈指数增长。我在我电脑上运行个60就十多秒都运行不出来。所以,对于这种大数据的,第一种递归方法枪毙。


int waysToStep(int n){
    if(n<=2)
    return n;
    if(n == 3)
    return n+1;
    else
    return (waysToStep(n-1)+waysToStep(n-2)+waysToStep(n-3))%1000000007;
}

(2)递归的优化

这种方法就是减少一些递归中,重复调用的情况。毕竟如果递归60,60就会递归到59、58、57,下一个递归就是58、57、56,这样子就会出现重复计算,造成时间浪费。但是只要把计算出来的数,贴上标签,再第二次递归的时候,就直接调用那个数组里的存在的计算出来的数,就会减少一些重复计算的时间啦。用这个可以通过计算,不过处理这种大数据的问题,还是要要注意栈溢出的问题,这里把数据弄成long long 就可以了。

long long checker(long long* flag, int n)
{
	if (n <= 2)
		return n;
    if(n == 3)
        return n+1;
	if (flag[n])//存在
		return flag[n];
	flag[n] = (checker(flag,n-1) + checker(flag,n-2) +checker(flag,n-3))%1000000007;//不存在则计算
	return flag[n];
}
int waysToStep(int n){
    if (n <= 2)
		return n;
    if(n == 3)
        return n+1;
   long long *flag = (long long*)calloc((n + 1),sizeof(long long));//动态初始化一个数组
    return checker(flag, n);
}

就是时间太长了。。。
在这里插入图片描述

(3)动态规划

dp[i]的含义就是走i个台阶的最多的方法。因为只有三种爬楼梯方法,所以只能从dp[i-1],dp[i-2],dp[i-3]爬上去。初始化的条件我们上面已经列出来了。如果对动态规划不怎么明白的,可以看一下我上一篇博客。戳这里前往所以这个动态规划的迭代是:

dp[i] = dp[i-1] + dp[i-2] + dp[i-3];

int waysToStep(int n){
    long long dp[10000000];
    int i,j;
    dp[0] = 1;
    dp[1] = 2;
    dp[2] = 4;
    if(n<=3)
    return dp[n-1];
    for(i=3;i<=n;i++)
    {
        dp[i] = dp[i-1]+dp[i-2]+dp[i-3];
        if(dp[i] > 1000000007)
            dp[i] = dp[i]%1000000007;
    }
    return dp[n-1];
}

看起来很标准的动态规划了,但是某个大佬吐槽我的的空间复杂度还是太大了,不是最优的解,然后就出现了第四种方法,究极节约时间空间的方法。
在这里插入图片描述

(4)究极优化的动态规划

其实看出来,定义这么大一个数组,空间复杂度为O(n),其实里面重复就在调用三个数,因为有三种方法嘛。我们直接用三个数代替他,然后挨着递加一下,岂不是很香香??qwq出现了,究极简化怪:

int waysToStep(int n){
    long long a=1,b=2,c=4,sum;
    int i;
    if(n<=2)
    return n;
    if(n == 3)
    return n+1;
    for(i=3;i<n;i++)
    {
        sum = (a+b+c)%1000000007;
        a = b;
        b = c;
        c = sum;
    }
    return sum;
}

究极香香!!完美。
在这里插入图片描述

代码的优化方法总结:

1.总的来说,就是先暴力递归,想到暴力解决的办法,尝试做出来。
2.再通过优化,减少时间复杂度,达到能通过的样子。
3.能够用动态规划的,可以尽量使用,最近觉得动态规划做题很香香。
4.优化以后,能够最终优化掉空间复杂度,把代码使用达到真正的简洁,那就是大佬,真正会使用动态规划了。

qwq希望这篇博客能帮到您,本篇博客由一位大佬指导代码完成,如发现类似博客极可能是那位大佬,请狠狠地点个赞,谢谢咩~

猜你喜欢

转载自blog.csdn.net/qq_46293423/article/details/104909565