动态规划的介绍与递归的区别

动态规划的定义:
要解决一个复杂的问题,可以考虑先解决其子问题。这便是典型的递归思想,比如最著名的斐波那契数列,讲递归必举的例子。

     斐波纳契数列的定义如下:F(0)=1,F(1)=1, F(n)=F(n-1)+F(n-2)(n>=2,n∈N*) 用递归可以很快写出这样一个函数,咋一看真牛逼,几行代码就搞定了

代码:
int fib(int i)
{
if(i <= 1)
{
return 0;
}
return fib(i - 1) + fib(i - 2);
}
1
将该函数的执行过程用图表示出来,就会发现fib4执行了一次,fib3执行了两次,fib2执行了三次,fib1计算了5次,重复的次数呈现爆发增长,接近与指数级。如果n取得足够大,暂且不说费时的问题,直接就会因为递归次数太多,函数堆栈溢出而程序奔溃。
那么很快就有人想到,用一个数组来保存曾经计算过的数据来避免重复计算。这种思想便是动态规划!

递归的求解子问题,再将它们的解组合起来,求出原问题的解。而动态规划与之相反,动态规划应用与子问题重叠的情况,即不同的子问题具有公共的子子问题(子问题的求解是递归进行的,将其划分为更小的子子问题)。在这种情况下,分治方法会做许多不必要的工作,他会反复求解那些公共子子问题。而动态规划对于每一个子子问题只求解一次,将其解保存在一个表格里面,从而无需每次求解一个子子问题时都重新计算,避免了不必要的计算工作。
动态规划的应用场景:
动态规划方法一般用来求解最优化问题。这类问题可以有很多可行解,每个解都有一个值,我们希望找到具有最优值的解,我们称这样的解为问题的一个最优解,而不是最优解,因为可能有多个解都达到最优值。
我们解决动态规划问题一般分为四步:
1、定义一个状态,这是一个最优解的结构特征
2、进行状态递推,得到递推公式
3、进行初始化
4、返回结果
下面我们通过几个例子来演示一下这个过程:

[编程题]斐波那契数列
热度指数:374145时间限制:1秒空间限制:32768K
算法知识视频讲解
大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0)。
n<=39
这个题目相信大家都很熟悉:
在我们学习递归的时候应该都见过:我们先写一个递归版本的。
代码如下:

class Solution {
public:
int Fibonacci(int n) {
if(n<=0)
return 0;
if(n == 1 || n == 2)
return 1;
return Fibonacci(n-1)+Fibonacci(n-2);
}
};
这个代码的时间复杂度是2^n,也就是指数递增,我们经过测试当求一个很大数字的时候,运行就会出错,没错就是溢出。
下来我们用动态规划来求解一下这个问题:
class Solution {
public:
int Fibonacci(int n) {
//先申请一个空间来保存解
vector f(n + 1, 0);
//初始化
f[0] = 0;

    if (n <= 0)
        return f[0];
    f[1] = 1;
    if (n == 1)
        return f[1];
    f[2] = 1;
    //状态递推
    for (int i = 3; i <= n; i++)
    {
        f[i] = f[i - 1] + f[i - 2];
    }
    //返回结果
    return f[n];
}

};

你可能感兴趣的:(c++)