算法之动态规划

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];

}

以上是关于算法之动态规划的主要内容,如果未能解决你的问题,请参考以下文章

Python <算法思想集结;之抽丝剥茧聊动态规划

算法专题 之 动态规划

五种常用算法之二:动态规划算法

豁然开朗经典算法之动态规划

LeetCodeLeetCode之跳跃游戏——动态规划+贪心算法

经典算法研究系列 之 动态规划算法