动态规划—入门篇

对于动态规划这类算法思想是,把问题划分为小的子问题,再利用递归或者递推的方式进行求解。但是对于一般的递归类算法,可能会多次相同的求解子问题,这就造成时间上的浪费,所以我们的一种思想就是把在前面求得的子问题储存起来,以便后面要用的时候直接调用即可。

最经典、最简单的动态规划莫过于斐波那契数列,以下就以此为例子展开说明。

实践是检验真理的唯一标准 码上

#include<iostream>
using namespace std;
int F(int n)
{
    if (n < 0)return 0;
    if (n == 0 || n == 1)return 1;
	//设置的数列前两项为1、1,即整个数列为1、1、2、3、5、8…… 
    else return F(n - 1) + F(n - 2);
}
int main()
{
    int n = 0;
    scanf("%d", &n);
	/*可能会有同学疑惑为啥用scanf\printf,cin\cout不香吗?
	之前写算法的时候就是因为这个点超时了,所以不香了,就养成了这个习惯 */ 
    printf("%d\n", F(n));
    return 0;
}

测试样例 5、15、45

  

可以看到时间增加得很快,当项数达到100的时候,基本已经很难等到它了(如果大家不信的话,可以copy两段代码分别测试一下),效率很低下。

问题一:为什么求解斐波那契数列算是动态规划?假设,我们要求得第7项的值时,我们要先求得第3、4、5、6项,每一项都是由前面两项相加得来,是由前面的子问题一步一步得出结果,而不是靠什么公式,一下子算出来。

问题二:为什么递归算法时间复杂度会这么高?我们开始读代码,一步步来执行。当求解第3项时我们要用到第1、第2项;当求解第4项时我们要用到第2、第3项;当求解第5项时我们要用到第3、第4项,以此类推……假设我们要求第5项时,可以发现第3项求了两次,第2项求了3次(这还只是求第5项),所以每次求后面项的时候都要把前面所求的都重复再求一遍,造成了时间上的浪费。

 试想以下,如果我们把前面求得的项储存起来,就解决了重复计算的问题,就不会造成了时间上的浪费。可以这么理解,动态规划的本质就是用空间来换取时间。

所以我们转到递推算法。


递推算法:

#include<iostream>
using namespace std;
int F(int n)
{
    if (n < 0)return 0;
    if (n == 0 || n == 1)return 1;
    else 
    {
    	int f1=1, f2=1, f3=1;
    	for(int i=2;i<=n;i++)
    	{
    		f1=f2;
    		f2=f3;
    		f3=f1+f2;
		}
		return f3;
	}
}
int main()
{
    int n = 0;
    scanf("%d", &n);
    printf("%d\n", F(n));
    return 0;
}

测试样例:45

可以看到时间减少了很多。我们把后面求得的项赋予前面,即存储起来,因为前面的项不会再被用到,所以可以覆盖,只是用到前面两项而已。

看完本篇的同学算是开始步入动态规划了,喜欢的同学可以关注,希望能帮助到你们!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值