学习近况
在我还没完全掌握贪心算法并且菜的一批我都不相信我自己能做出来题目的同时,动态规划的训练又开始了。跟往常一样,我决定努力学习动态规划,然鹅天不随人愿,我即使上课好好听讲,也用力在看例题,但四,我仍然想知道你们到底是怎么相出这个题的想法来的!!
相信有一部分新手刚刚接触动态规划的时候也是这样,觉得难以理解,但是有些前辈说,只要真正理解了,就会觉得它很简单。
我觉得学习这个算法,只看各种理论是不够的,还要看各种例题,只有两者相结合,才能学的更好。
关于动态规划的思考
以我最近对动态规划的学习,我觉得动态规划的核心就是递推。做过了好多的题,看来好多例题之后,觉得每个题基本都离不开递推公式。
如这个万能公式f[n]=f[n-1]+d;
其中最经典的就是斐波那契递推公式。
public int fib(int n)
{
if(n<=0)
return 0;
if(n==1)
return 1;
return fib( n-1)+fib(n-2);
}
在这次的练习题之中,也有一个需要用到斐波那契题目,就是:编程计算蜜蜂从蜂房a爬到蜂房b的可能路线数。刚开始做这个题目的时候,我还没反应过来要用斐波那契数列,知道我在不经意间(嘤嘤嘤忍不住查了答案)终于知道了要用斐波那契数列来做,在经过我的深刻思索,终于写出了代码。在我满心期待可以ac的时候,却是超时。
所以这又引出了另外一个关于动态规划的重点:重复计算,耗时更长。
为了解决这一问题,有人就提出了记忆化搜索的方法。听起来十分高大上,了解过后才知道,原来就是用数组来存储那些经过计算的量,等使用的时候直接拿过来用,就省去了重复计算的要花费的时间,大大避免了超时。老师也称之为用空间来换取时间,也是,用数组来存放数据必然会花费较多的空间。
上面这一方法也被称为自顶向下的备忘录法。将已知的东西存储起来就像是存放在备忘录一样随时可以被使用,确实是很像备忘录的作用。
同时,还有另外一种动态规划,那就是自底向上的动态规划,适用于从头思考条件越来越多,越难越思考全面的情况。
动态规划原理
状态:
动态规划跟贪心算法一样,需要有最优子结构。不同的是,贪心算法的每一步都是可以独立求得结果的,动态规划不一样,它的子问题可以有不同的状态,并不是类似的。所以在求解动态规划问题的时候,会涉及到状态的转移,这里就是最重要最关键的一步,因为递推公式往往就是根据这一步得出来的。
在例题最长子序列中的状态转移就是从以a[i-1]结尾的序列到以a[i]结尾的序列的状态变化,而这步就得出了序列长度的转移。
决策和无后效性:
由一个状态转换为另一个状态,是通过决策来实现的。无后效性则是说状态转移后无法对后续的状态产生影响。
就目前而言,我最常用的状态转移方法就是f[i]=max(f[i-1],?)+d;用求哪个比较大或者比较小的方法来过度到下一个状态去。
动态规划一般思路:
- 将原问题分解为子问题。知道某个子问题的求解方法就能推出整个问题的求解方法。
- 确定状态。了解各个子问题的各阶段状态,找出决策方法。
- 确定初始值。不用多说
- 确定状态转移方程,一般到这一步就可以写出代码的雏形了。只要再将其完善,ac水到渠成。
动态规划是比较难的一个算法之一,我一定要好好学习动态规划,争取不落下太多!同学们做题又快又好,我难道就是没有天赋吗?都说笨鸟先飞,可是我却又笨又懒,哎,真希望能够克服自己。