这一篇是之前在做过一些dp题目之后,总结了一些东西,文章还没最后完成
动态规划
1、前言
动态规划(Dynamic Programming
)本身是属于运筹学的一个分支。是解决决策过程最优解的数学方法,
常常做到动态规划问题,想记录下做过的dp题,遇到简单的还可以想出来一些,但是常常都是有点难度,而且动态规划问题很常见,在一些大厂的笔试面试题中,今天来总结下动态规划。
2、介绍
动态规划有个很明显的特点就是可以解决重复的子问题,就是将多次重复计算的子部分省掉,只计算一次,将其记录起来,提高效率。还有一个很重要的特点是存在最优子结构-子部分中有个部分是符合题目的最优解。当然这个结果是在比较之后得出的。
3、分类
在百度百科上有着详细的分类,遇到比较多的是背包问题,在下面的举例中的也大多是背包问题,背包问题就是简单来说,就一个背包,装东西,怎么装,在满足什么条件去装,从而得到不同的最优结果。
4、使用场景以及怎么使用
使用场景
首先是什么时候会用到动态规划,一开始我也会模糊这个使用。但总的来说就是遇到一些求最值问题,或者是最优解,往往动态规划都是可以用的。
同时记住要使用动态规划的前提是必须满足最优化原理(简单说这个解一定是最屌的解,最优解)和无后效性(不受前面影响)。
最优化原理(最优子结构性质):一个最优化策略具有这样的性质,不论过去状态和决策如何,对前面的决策所形成的状态而言,余下的诸决策必须构成最优策略。简而言之,一个最优化策略的子策略总是最优的。一个问题满足最优化原理又称其具有最优子结构性质。
过程的历史只能通过当前的状态去影响它的未来的发展,不受之前的影响,当前的状态已经是之前的总结了,未来只能是通过现在去影响了。这个性质称为无后效性。
使用步骤
概念
动态规划中重要的三个概念有最优子结构、边界和状态转移方程。理解好这三个,任何动态规划问题就可以很好很容易解决的。
最优子结构-指的是不走最后一步或者走最后一步的最优子情况是怎么样的。
边界是每一个动态规划问题要想有解必须存在的,没有边界就没有答案,就是到某一个解的时候,其没有子解了,它自己已经是最小的了。
状态转移方程是最重要的,常常看到下面类似这种方程,就是状态转移方程。
步骤(当确定了使用动态规划之后)
1、找到状态
(小规模问题的数学表示)状态表示阶段开始时的客观条件,当前处于什么的条件
2、最小状态是什么
这个也是指边界(最小规模的问题)也是逆推的终点。
3、列出状态转移方程
(大规模问题如何转化为更小的问题)确定过程由一个状态转移到另一个状态的演变过程。这个也是最重要的,最难的。
4、要求的返回值是什么
题目要求返回什么,就将结果解答出来。因为有时最后的最优状态不是所需的,可能需要输出其他的结果,所以要看清楚题目要求。
5、动态规划解题思维
有两种思维,简单的就是从头到尾,还有从尾到头。
自顶向下
基本会用递归,大范围开始推算到小范围(后面的最优到最前的最优),注意不断保存着中间结果,避免了重复计算。这里是和运筹学中的逆推解法大同小异。意思是从最终状态开始,逆着实际过程的进展方向逐段求解。直到第一个阶段为止。
下面举例是熟悉的走楼梯
一个人每次只能走一层楼梯或者两层楼梯,问走到第100层楼梯一共有多少种方法。
int[] dp= new int[100];/*用于保存中间结果否则会重复计算很多重复的子问题*/
int DP(int n)
{
//1和2是边界条件
if(n == 1)
return 1;
if(n == 2)
return 2;
//状态转移方程
dp[n] = DP(n-1) + DP(n-2);
//返回的结果
return dp[n];
}
自底向上
自小推到大,从小范围到大范围,运筹学中的顺推法,与上面只是方向不一样。
格式如下,常常伴随着for遍历
for(int i= 0; i < max; i++){
dp[i] = dp[i-1]+dp[i-2]
}
6、例题
题目有以下这些推荐---
#背包问题
背包问题也分为了0-1背包和完全背包和多重背包问题
0-1背包问题
0-1背包就是每个物品只能用一次,重量价值都是一一对应,放的重量也是有限的。然后求出最大价值的放置方法
#416
完全背包
完全背包问题是物品是无次数限制。其他和0-1一样
多重背包
多重背包是物品有次数限制,比如2、3、4这样的
#5
#983
7、最后
动态规划就是解决冗余的。
借鉴了一些文章如下,感谢,有什么不对的地方大家多多指教,多多留言,觉得写的还可以点个赞hh
程序员小灰
知乎