算法之动态规划
Posted 程序员的小站
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了算法之动态规划相关的知识,希望对你有一定的参考价值。
动态规划在寻找有很多重叠子问题的情况的最佳解时有效。它将问题重新组合成子问题。为了避免多次解决这些子问题,它们的结果都逐渐被计算并被储存,从简单的问题直到整个问题都被解决。因此,动态规划储存递归时的结果,因而不会在解决同样的问题时花费时间。
动态规划只能应用于有最佳子结构的问题。最佳子结构的意思是局部最佳解能决定全域最佳解(对有些问题这个要求并不能完全满足,故有时需要引入一定的近似)。简单地说,问题能够分解成子问题来解决。
以上就是官方的定义,相信很多初学者都是一脸懵逼的模样。学过分治法的同学会更加懵逼,这个不就是分治法的思想嘛。那么动态规划算法的核心思想是什么呢?其实就是小问题的解能否重复使用,帮助求解更大的问题。通俗点,就是计算机里的所谓“用空间换时间”。下面举个很简单的例子来帮助理解一下。
大人:“1+1+1+1 等于几??
小孩:数了4根手指以后,4
大人:“再加一个1 等于几??
小孩:5
大人:怎么这么快算出了5
小孩:你只是在后面又加了一个1而已
大人:恭喜你,你使用了动态规划的思维。你在求解5这个结果的时候,利用了前面4的结果。
有人把实现动态规划的具体步骤总结为如下三步:
1 建立状态转移方程
2 缓存并复用以往结果
3 按顺序从小往大计算
下面列举一些比较经典的动态规划算法例子。
第一种
有n个阶梯,一个人每一步只能跨一个台阶或是两个台阶,问这个人一共有多少种走法?
建立方程式:f(n) = f(n+1) + f(n+2) ; n起始为最大台阶数减2的值。其实就是一个斐波那契数列,只不过这里要反过来遍历,从最大台阶向小的台阶开始计算。
以10个台阶为例,代码如下
private static int climbStairs(int n) {
//由于是0到n, 所以数组长度是n+1
int [] cache = new int[n+1];
cache[n] = 1;
cache[n-1] = 1;
//从大台阶数到小遍历
for(int i=n-2; i>=0; i--) {
cache[i] = cache[i+1]+cache[i+2];
}
return cache[0];
}
第二种
求一个给定数列的最大连续子序列和,例如:1,-3,5,4,-2,10的最大连续和子序列是{5,4,-2,10},和是17。
该题难点:正确构建方程式,需要仔细推敲:连续2字!!!!
提示:先求解以某个位置结尾的连续序列组合的最大值,然后依次推出
方程式:f(n) =max{f(n-1)+a[n],a[n]}
代码如下
private static int maxsum(int[] a) {
int[] cache = new int[a.length];
cache[0] = a[0];
//递推
for(int i=1;i<a.length;i++) {
cache[i] = m(cache[i-1]+a[i],a[i]);
}
//求最大的值
int max = cache[0];
for (int i : cache) {
if(i > max) {
max = i;
}
}
return max;
}
第三种
0-1背包问题:
一个贼进了商城,现有如下物品可偷:音响 3000美元 4斤、笔记本 2000美元 3斤、吉他 1500美元 1斤。贼有一个能装4斤重的背包,求能偷走的最大价值。(每个物品只能装一次)
分析:该题难点是状态维度变成了二维。需要按物品和背包建立一个二维表,并按照题目条件在每个格子中求出最大值,如图。
代码如下
private static int package01() {
//保存物体重量和价值
int[] v = {3000,2000,1500};
int[] w= {4,3,1};
//记录各种状态维度下的最大值
int[][] pack = new int[4][5];
//递推算出所有值
for(int i=1;i<4;i++) {
for(int j=0;j<5;j++) {
if(j<w[i-1]) {
pack[i][j] = pack[i-1][j];
}else {
pack[i][j] = max(v[i-1]+pack[i-1][j-w[i-1]],pack[i-1][j]);
}
System.out.print(pack[i][j]+" ");
}
System.out.println();
}
return pack[3][4];
}
以上是关于算法之动态规划的主要内容,如果未能解决你的问题,请参考以下文章