前言:
学习计算机,有一项是你永远逃不掉的,那就是刷题。。。(好像其他科目也是一样)而刷题呢,又肯定离不开那万恶的OJ,从注册账号开始,我便每天受着这破玩意儿的折磨(此处刷过OJ的人会理解我的),但刷着刷着,我惊奇的发现里面有好多问题都与动态规划有关,便搜了搜看了看大佬们的博客,来这里总结一下。
动态规划:
维基百科说得好,动态规划就是通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。
说白了就是:
1. 这个问题可以分解为子问题
2. 原问题与子问题有关系
3. 子问题可以求解
说到这里,你是不是想到了递归,想到了数学归纳法,其实他们都是相通的。(哇哦,瞬间觉得学科之间原来真的是有联系的)
看到这里,你是不是觉得动态规划挺简单的嘛,就是三个步骤嘛:
1. 把原问题分解为子问题
2. 找到原问题与子问题的关系
3. 记录下子问题的解
emmm,就是这三个简单的步骤,难倒了多少好汉啊(也包括我),下面我们就通过分析一些使用动态规划解决的问题来摸索这三步怎么找。
动态规划的常见应用:
1. 斐波那契数列:
1 1 2 3 5 8 13。。。
我们要求第n个数,即f(n)
a. 分解问题:求f(n)就是求f(n - 1) 与 f(n - 2)
b. 找关系:f(n) = f(n - 1) + f(n - 2)
c.找个数组把f(n)存起来,这样调用时就不用再次计算了
这样,在f(1) 与 f(2) 已知的情况下,我们便可以求得f(n)。
看到这里,你可能会说这不是明显的递归吗,怎么是动态规划呢?这里便要提出动态规划与递归的不同,若用递归,求f(100)就要求f(99) 与 f(98), 求f(99)就要求f(98) 与f(97),此时f(98)算了两遍,而使用动态规划,它存储了f(98),后面用的时候便不需要再重新计算,能够省下来许多时间,这便是传说中动态规划记忆化存储的性质。
具体可以看看2011年06月07日 19:23:00 deepit的博客:https://blog.csdn.net/DeepIT/article/details/6530282
2. 钢条切割问题
设f(n)为长度为n的钢条切割后得到的最大价值
a.分解问题:
求f(n)即需要知道f(n - 1),f(n - 2), ....及p(n), p(n -1), p(n -2)....
b.找关系:
f(n) = max(p(n), f(1) + f(n -1), f(2) + f(n -2), ......., f(n -1) + f(1) )
c.存储f(n),节省时间
这样便可找到最值。
int cut(int []p) { int *r=new int[p.length+1]; for(int i = 1; i <= p.length; i++) { int q = -1; for(int j=1;j<=i;j++){ q = max(q, p[j-1] + r[i-j]); } r[i]=q; } return r[p.length]; }
3. 小朋友过河问题
在一个夜黑风高的晚上,有n(n <= 50)个小朋友在桥的这边,现在他们需要过桥,但是由于桥很窄,每次只允许不大于两人通过,他们只有一个手电筒,所以每次过桥的两个人需要把手电筒带回来,i号小朋友过桥的时间为T[i],两个人过桥的总时间为二者中时间长者。问所有小朋友过桥的总时间最短是多少。
输入:
两行数据:第一行为小朋友个数n
第二行有n个数,用空格隔开,分别是每个小朋友过桥的时间。
输出:
一行数据:所有小朋友过桥花费的最少时间。
未完待补充