这个动态规划系列主要是给自己平时的练习做个记录,写博客的过程也是一个熟悉与提高的过程,记得有一句话,提高动态规划能力最好的方法就是思考每一个状态和状态转移方程是怎么来的,而写博客这么个讲述的过程,就要求我能真正理解到那样一种思维过程,其次也是方便后来的初学者的学习吧,我个人是挺头疼动态规划的,前两天接触DP 然后去参加一个校内的算法比赛 有道很简单的DP 居然没做出来 事后看别人代码才觉得 原来是这样 代码10几行就搞定了。
好了 废话不多说 开始正文
动态规划(dynamic programming)是运筹学的一个分支,是求解决策过程(decision process)最优化的数学方法。20世纪50年代初美国数学家R.E.Bellman等人在研究多阶段决策过程(multistep decision process)的优化问题时,提出了著名的最优化原理(principle of optimality),把多阶段过程转化为一系列单阶段问题,利用各阶段之间的关系,逐个求解,创立了解决这类过程优化问题的新方法--动态规划(摘自360百科)
动态规划类型的问题有三个关键的属性 1.状态。2.基准情况 3.状态转移方程
重点就在这句话:把多阶段过程转化为一系列单阶段问题,利用各阶段之间的关系,逐个求解
解动态规划题的第一步也是最重要的一步就是定义正确的状态,状态决定了子问题间的关系,而关系又决定了如何得出状态转移方程
状态的定义要遵守两个可使用动态规划的准则:
(1)问题具有最优子结构性质。如果问题的最优解所包含的 子问题的解也是最优的,我们就称该问题具有最优子结 构性质。
(2) 无后效性。当前的若干个状态值一旦确定,则此后过程的演变就只和这若干个状态的值有关,和之前是采取哪种手段或经过哪条路径演变到当前的这若干个状态,没有关系。
第二点要特别注意,刚学动态规划的时候特别容易犯的一个错误就是定义了一个不满足“无后效性”的状态,而通常的解决方案是对状态添加一个维度或多个维度以解决或者改变定义使之包含某种路径,所以有时候维度的大小也一定程度上反应了问题的复杂程序。对于这个问题我将用一个经典例子来说明
最长上升子序列问题(LIS)
描述:给出一个序列a1,a2,a3,a4,a5,a6,a7....an,求它的一个子序列(设为s1,s2,...sn),使得这个子序列满足这样的性质,s1<s2<s3<...<sn并且这个子序列的长度最长。输出这个最长的长度
很可能第一次接触动态规划的人会这样定义一个状态,dp[i]:长度为i的序列的最长上升子序列的长度,但这是有问题的,假设你要从dp[i-1]得到dp[i] 那么就要考虑到dp[i-1]中的某个最长上升子序列是以什么结尾的 才能判断第i位是否可以和dp[i-1]中某个最长上升子序列组成一个新的长度+1的最长上升子序列,明显不满足“无后效性”,而解决方式就是将”以哪一位的元素结尾”包含进我们的状态 于是我们定义dp[i]为以第i位元素结尾的最长上升子序列的长度,就满足了“无后效性”
相应的 基准情况 dp[1]=1
状态转移方程:dp[i]=max{1,dp[j]+1}(1<=j<=i-1,且第i位的元素大于第j位的元素)
下面正式开始我们的第一个动态规划程序
斐波那契数列(Fibbonacci Number),又称黄金分割数列,指的是这样一个数列:0、1、1、2、3、5、8、13、21、34、……在数学上,斐波纳契数列以如下被以递归的方法定义:F(0)=0,F(1)=1,F(n)=F(n-1)+F(n-2)(n≥2,n∈N*)
现在让你编写一个程序 根据输入的n,输出对应的菲波拉契数,即f(n)
这个程序可以说是最简单的动态规划了,简单在哪里呢?简单在 题已经把状态f(n):第n位的菲波拉契数,基准情况F(0)=0,F(1)=1,转移方程F(n)=F(n-1)+F(n-2)(n≥2,n∈N*),这三大要素都告诉你了,而现在你只需要按照定义实现就好了,很简单 对吧?
直接附上代码:
#include"stdio.h" int main() { int n = 0, i; long long a[51]; a[0] = 0; a[1] = 1; for(i = 2; i < 51; i++) { a[i] = a[i - 1] + a[i - 2]; } while(1) { scanf("%d", &n); if(n == -1)break; printf("%lld\n", a[n]);//注意格式,这是使用的是"%lld" } return 0; }
好了,第一篇结束,算是一个开篇吧,第一次写这种文章,也是刚接触动态规划,因为个人认为很多书籍的动态规划的学习曲线是很陡峭的,而我身为一个新人,也没多好的数学基础,相信我的一个学习经历的过程是相对来说平缓的,希望对大家有帮助,有时候文章中会经常引用其他大牛及百科的原话,每次都标注很麻烦,请原作者见谅。
另,菜鸡一枚,说的不对请指出。