一、什么是动态规划
1.官方定义
动态规划(英语:Dynamic programming,简称DP)是一种在数学、管理科学、计算机科学、经济学和生物信息学中使用的,通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。
动态规划背后的基本思想非常简单。大致上,若要解一个给定问题,我们需要解其不同部分(即子问题),再根据子问题的解以得出原问题的解。动态规划往往用于优化递归问题,例如斐波那契数列,如果运用递归的方式来求解会重复计算很多相同的子问题,利用动态规划的思想可以减少计算量。
2.动态规划的特点
看官方的解释大家应该都会比较懵,通俗的来讲,动态规划就是大事化小,小事化无。要解决一个大问题就将大问题拆解成小问题,通过逐个解决这些小问题,最终解决问题。
动态规划可以说是递归方法的一种优化,在递归问题中,我们需要进行大量的重复计算过程,时间复杂度极高。于是就有了将计算过的结果保存起来,每一个子问题只需要计算一次就好。动态规划实际也是使用 空间换时间 的一种做法。于是动态规划问题就具有这样的特点:
- 问题可以逐步拆解
- 所有子问题只需解决一次
- 对子问题进行存储
在解决动态规划问题时,通常就是以下三个步骤:
- 状态定义
- 初始化
- 状态转移方程
如果你还不是很懂这些个名词的意思,那就通俗的讲一下:
状态定义 :在我们使用空间换取时间时,当然要定义一个可以存储每一个子问题的结果的集合,那对于这个集合的每一个元素代表的意义一定要明确。当我们选定存储集代表的意义时,就完成了状态定义。
初始化 :最大的问题逐步分解为小问题,从最小的问题开始解决时往往我们是知道结果的,就需要将最原始的子问题的值初始化出来。
状态转移方程 :通常这也是最难想出来的一部分,我们从小问题逐步解决,状态转移方程就体现了“逐步”这一过程,列出状态转移方程可以经过多次套用由最小的问题逐步解决。
要完全理解还是要通过题目加深印象,接下来由简到难逐步来几个题目帮助理解:
二、题目练练手
1.斐波那契数列
大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为
0)。
n<=39
int Fibonacci(int n)
斐波那契数列:
1 1 2 3 5 8 13 21 …
斐波那契数列应该是递归的入门题目了,但使用递归显然耗时太多,今天就换一种解法,使用动态规划来解决。
我们按照三个步骤来:
状态定义:
首先,我们定义数组dp[ ]来存储结果,那么每一处应该存放的数字的含义是什么呢?我们要求的是数列的第n项,那不如就定义数组的第 i 处数据表示的就是斐波那契数列的第 i 项。
初始化:
我们知道斐波那契数列求解的方法是前两项之和,因此在初始化时,至少初始化前两项。题目中表示第 0 项为 0 而第一项就是 1 这就初始化好了前两项。
状态转移方程:
知道第零和第一项,怎么求第二项?当然是加起来,也就是
第二项: dp[2] = dp[1] + dp[0] ;
第三项: dp[3] = dp[2] + dp[1] ;
第四项: dp[4] = dp[3] + dp[2] ;
…
第n项: dp[n] = dp[n - 1] + dp[n - 2] ;
这就得到了我们的状态转移方程
接下来就是顺理成章的写代码了
代码:
public class Fibonacci {
public int Fibonacci(int n) {
if(n == 0)
return 0;
if(n == 1 || n == 2)
return 1;
//因为有第0项,所以在创建时要创建n+1长度
int[] dp = new int[n + 1];
//初始化
dp[0] =</