动态规划本质上就是一种带有数组的分治思想
那么动态规划什么时候用呢?
类似于这种
可以发现,当计算 f(n) 的时候,需要用到 f(n 2 1),当计算 f(n - 1) 的时候,还是
需要用到 f(n - 2),这样,一个子问题就要计算 k 次,显然增加了时间复杂度,这种时候
就需要用到动态规划了,将所有的子问题都用一个数组记录下来,避免重复计算,可以说,
动态规划的精髓就在于数组记录状态和状态转移方程。
DP:1、分析子问题的依赖关系(子问题:重叠)
2、采用自底向上的方法依次求解
3、从最小规模求解,一直到最大子问题
注:子问题不管是否用到,都要计算出来保存起来
子问题放到表格里面。
一、斐波那契数列
int get(int n)
{
if(f[n])
{
return f[n];
}
f[n] = get(n - 1) + get(n - 2);
return f[n];
}
在使用前将 f[0] 和 f[1] 初始化为 1 即可。
二、K级图问题
K 级图定义为:每一级有若干个结点,一共有若干个级,每一级只能由上一级指入,
只能指向下一级,且从一级前往下一级的节点会有 h 的权重,并且第一级和最后一级
都只有一个节点,求从开始节点到最后节点的最短路径。
图示:
可以看到,要想求 7 号的最短路径,要先求出 4、5、6 的最短路径,如果采用递归的
方式,那么势必要计算 3 次 2号节点的最短路径,或许这对于这里的二号节点是可以接受
的,但是当级数增加,级内节点增加,那么一个 k 号节点的重复计算次数便不可估计,所以
递归方法便变得不可接受了,如果用数组将 k 号节点的最短路径保存起来,当用到的时候
取出来,这样便可以避免重复运算了,这就是动态规划的思维,所以这道题可以这样求解
1、定义一个节点类型
struct Node
{
int min_path;
Node * from;
};
struct point
{
int x, y;
};
同时定义一个二维数组或者用 map<point, int> 来保存点与点直接的消耗
2、每一节点的 min_path = min(上一级各点 min_path + 上一级各点到该点的消耗)
3、在计算 min_path 的同时更新该点的 from 为使得 min_path 最小的上一级点的
编号
4、最后的 min_path 就是该点的 最短路径长度,最短路径只需要根据 from 字段
依次向前寻址即可
当然,在做这一切之前将最简单的子问题 0号的min_path 初始化即可
三、格路问题
格路问题定义:对于一个给定的网格,每一条路径上都有一个权值,求从(0,0)到
(m,n)的最短路径
这个问题本质上与 K 级图类似,求解(m,n)的最短路径,由于最短路径只能从
上或者右走,所以(m,n)有两个子问题,左边或者下边,所以只需要求解 min(下边
最短路径+向上的边,左边最短路径+向右的边),同样,子问题再分解成为子问题,
这时你会发现,有很多点的子问题都要重复计算,所以,可以采用二维数组将这个最短路
径的数值保存起来,即用即取,于是
1、定义一个结构体
struct Node
{
int min_path;
Node * from;
};
2、初始化
最简单的子问题是 第0行 和 第0列,因为他们只有一个来源,因此只需要不断加
起来计算 min_path 就好
3、对于一般的点来说,进行两重循环,按照下面的状态转移方程计算
min_path = min(下边最短路径+向上的边,左边最短路径+向右的边);
4、输出(m,n)的 min_path 即可,求最短路径的方式只需要从(m,n)开始不断的
“from” 即可。