算法6:c++实现动态规划
1、我们拿 斐波那契数来说,斐波那契数的定义为f(n)=f(n-1)+f(n-2)。我们有以下的方法进行计算,分别是暴力递归,记忆性递归,和动态规划
2、暴力递归方法(缺点是这个方法一定n比较大的时候容易造成时间复杂度很高):
class solution
{
public:
int fbnq(int n)
{
if (n == 0)
{
return 0;
}
if (n == 1)
{
return 1;
}
else
{
return fbnq(n - 1) + fbnq(n - 2);
}
}
};
3、记忆性递归方法(实际上是通过增加空间复杂度来减少时间复杂度的方法,增加一个记忆来记录数据的方法)
class solution { public: int fbnq(int n, vector<int> arr) { if (n == 0) { return 0; } if (n == 1) { return 1; } if (arr[n] != 0) { return arr[n]; } else { return fbnq(n - 1,arr) + fbnq(n - 2,arr); } } int fbnqresult(int a) { vector<int> f(a+1,0); return fbnq(a,f); } };
这种方法在力扣上面仍然还是超时的,因为虽然减少了时间复杂度,但是还是不满足力扣对时间的要求
4、动态规划(这方法是时间复杂度要求最好的算法之一)
class Solution { public: int fib(int n) { if(n==0) { return 0; } vector<int> f(n+1,0); f[1]=1; for(int i=2;i<n+1;i++)//动态记录的过程 { f[i]=(f[i-1]+f[i-2]); } return f[n]; } };
3、动态规划三步走:
动态规划,无非就是利用历史记录,来避免我们的重复计算。而这些历史记录,我们得需要一些变量来保存,一般是用一维数组或者二维数组来保存。下面我们先来讲下做动态规划题很重要的三个步骤,
如果你听不懂,也没关系,下面会有很多例题讲解,估计你就懂了。之所以不配合例题来讲这些步骤,也是为了怕你们脑袋乱了
第一步骤:定义数组元素的含义,上面说了,我们会用一个数组,来保存历史数组,假设用一维数组 dp[] 吧。这个时候有一个非常非常重要的点,就是规定你这个数组元素的含义,例如你的 dp[i] 是代表什么意思?
(这里很重要的一个步骤是一般都是从后往前推到的)第二步骤:找出数组元素之间的关系式,我觉得动态规划,还是有一点类似于我们高中学习时的归纳法的,当我们要计算 dp[n] 时,是可以利用 dp[n-1],dp[n-2]…..dp[1],来推出 dp[n] 的,也就是可以利用历史数据来推出新的元素值,所以我们要找出数组元素之间的关系式,例如 dp[n] = dp[n-1] + dp[n-2],这个就是他们的关系式了。而这一步,也是最难的一步,后面我会讲几种类型的题来说。
学过动态规划的可能都经常听到最优子结构,把大的问题拆分成小的问题,说时候,最开始的时候,我是对最优子结构一梦懵逼的。估计你们也听多了,所以这一次,我将换一种形式来讲,不再是各种子问题,各种最优子结构。所以大佬可别喷我再乱讲,因为我说了,这是我自己平时做题的套路。
第三步骤:找出初始值。学过数学归纳法的都知道,虽然我们知道了数组元素之间的关系式,例如 dp[n] = dp[n-1] + dp[n-2],我们可以通过 dp[n-1] 和 dp[n-2] 来计算 dp[n],但是,我们得知道初始值啊,例如一直推下去的话,会由 dp[3] = dp[2] + dp[1]。而 dp[2] 和 dp[1] 是不能再分解的了,所以我们必须要能够直接获得 dp[2] 和 dp[1] 的值,而这,就是所谓的初始值。
由了初始值,并且有了数组元素之间的关系式,那么我们就可以得到 dp[n] 的值了,而 dp[n] 的含义是由你来定义的,你想求什么,就定义它是什么,这样,这道题也就解出来了。

浙公网安备 33010602011771号