看了一些网上讲的内容,先是看看知乎,再看看csdn,最后我看了看百度百科,呵呵。就一个动态规划问题,竟然兜兜转转找了这么多的网址,也是醉了,知乎上的人过于装逼,不好好说话,非得把清晰的问题讲的玄乎,csdn上讲的入门就是直接给你上代码,最后实在无奈,直接百度百科。
动态规划这个东西,听了好多次了,但是真正去了解,也就今天。记得研一上优化方法课的时候,老师给讲线性规划,纯粹了图过的心理。没有详细地把周边知识给整理起来,其实动态规划就是优化方法里面的一个内容。
动态规划,开始我对他的观点就是一个内存存储的分配(简直是傻啊)。其实看完一通,明白,动态规划,说白了就是决策问题,将一个大的问题拆分成好多小问题,然后去解决这些小的问题,然后这些小的问题之间存在着一些联系(这个就是将动态规划问题与分治问题区分的关键)。动态规划问题是为了求到每个子问题的最优,然后寻求到整个问题的最优。动态规划本质上就是优化问题(但是我有一个问题,采用动态规划,是否会陷入到局部最优,就是你看似把每个子问题的最优求到了,但是不一定就是全局最优?存疑!)
由于动态规划的这个性质,因此动态规划会在时间复杂度上有比较大的优势,但是在空间复杂度上存在比较大的问题,因此就相当于拿空间换了时间。
动态规划可以解决的问题也是多种多样的。
动态规划一般可分为线性动规,区域动规,树形动规,背包动规四类。
举例:
线性动规:拦截导弹,合唱队形,挖地雷,建学校,剑客决斗等;
区域动规:石子合并, 加分二叉树,统计单词个数,炮兵布阵等;
树形动规:贪吃的九头龙,二分查找树,聚会的欢乐,数字三角形等;
背包问题:01背包问题,完全背包问题,分组背包问题,二维背包,装箱问题,挤牛奶(同济ACM第1132题)等;
应用实例:
可以用到动态规划的地方很多,这次需要总结动态规划的原因,也是因为在牛客网上遇到一道编程题-背包问题。按照网友提供的思路,在百度上搜索背包九讲,可以学会相关的问题,但是可能本人天资愚钝,还是没能明白其中的道理。背包九讲另外一个问题就是纯粹是写作的人的自嗨,其中的思路纯粹是自己跳跃着的,讲的不是很好。
动态规划的本质
动态规划是一种解决问题的思想。这种思想的本质是,一个规模比较大的问题(假如用2-3个参数可以表示),是通过规模比较小的若干问题的结果来得到的(通过取最大,取最小,或者加起来之类的运算)所以我们经常看到的动态规划的核心——状态转移方程都长成这样:
动态规划的类型
在技术面试中经常出现的是矩阵型,序列型和双序列型。划分型,区间型和背包型偶尔出现。状态压缩和树型基本不会出现(一般在算法竞赛中才会出现)。
矩阵型:
以矩阵型动态规划为例,一般题目会给你一个矩阵,告诉你有一个小人在上面走动,每次只能向右和向下走,然后问你比如有多少种方案从左上走到右下 (http://www.lintcode.com/problem/unique-paths/)。这种类型状态表示的特点一般是使用坐标作为状态,如f[i][j]表示走到(i,j)这个位置的时候,一共有多少种方案。状态的转移则是考虑是从哪儿走到(i,j)这个坐标的。
什么样的题适合用动态规划
1. 求最大值/最小值
2. 求可不可行
3. 求方案总数
如果一个问题让你求出“所有的”方案和结果,则肯定不是使用动态规划。
- 求出所有具体的方案而非方案个数
- 输入数据是一个集合而不是序列
- 暴力算法的复杂度已经是多项式级别
- 动态规划擅长优化指数级别复杂度(2^n, n!)到多项式级别复杂度(n^2, n^3)
- 不擅长优化n^3到n^2
解决动态规划问题的步骤
1. 状态是什么
2. 状态转移方程是什么
3. 状态的初始值是什么
4. 问题要求的最后答案是什么
序列型:
序列型的动态规划,一般是告诉你一个序列;双序列的动态规划一般是告诉你两个字符串或者两个序列。
矩阵型动态规划规律总结
1. 确定递归函数意义,或者确定迭代的基本函数式
2. 确定大小范围(矩阵的长宽),设计动态规划矩阵
3. 确定递归终止状态,填表边界(自底向上时,从结尾填表,自顶向下,跟踪递归流程填表)
4. 确定要求的状态(矩阵的值),确定现在要求的状态(矩阵的值与之前一个状态之间的函数关系)
《LintCode动态规划题总结》这篇博客讲的很好,很全。值得关注。