算法(三):动态规划法

来源:互联网 发布:无法安装java 编辑:程序博客网 时间:2024/06/05 15:53

算法的基本思想

动态规划法将待求解问题分解成若干个相互重叠的子问题,每个子问题对应于决策过程的一个阶段,一般来说,子问题的重叠关系表现在对给定问题求解的递推关系(也就是动态规划函数)中,将子问题的解求解一次并填入表中,当需要再次求解此子问题时,可以通过查表获得该子问题的解而不用再次求解,从而避免了大量重复计算。

适用的情况

能采用动态规划求解的问题的一般要具有3个性质:
(1) 最优化原理:如果问题的最优解所包含的子问题的解也是最优的,就称该问题具有最优子结构,即满足最优化原理。

(2) 无后效性:即某阶段状态一旦确定,就不受这个状态以后决策的影响。也就是说,某状态以后的过程不会影响以前的状态,只与当前状态有关。

(3)有重叠子问题:即子问题之间是不独立的,一个子问题在下一阶段决策中可能被多次使用到。(该性质并不是动态规划适用的必要条件,但是如果没有这条性质,动态规划算法同其他算法相比就不具备优势)

动态规划算法的设计步骤

  • 找出最优解的性质,并刻画其结构特征;
  • 递归地定义最优值;
  • 以自底向上的方式计算出最优值;
  • 根据计算最优值时得到的信息,构造最优解;

算法实现的说明

动态规划的主要难点在于理论上的设计,也就是上面4个步骤的确定,一旦设计完成,实现部分就会非常简单。
使用动态规划求解问题,最重要的就是确定动态规划三要素:
1. 问题的阶段
2. 每个阶段的状态
3. 从前一个阶段转化到后一个阶段之间的递推关系。

递推关系必须是从次小的问题开始到较大的问题之间的转化,从这个角度来说,动态规划往往可以用递归程序来实现,不过因为递推可以充分利用前面保存的子问题的解来减少重复计算,所以对于大规模问题来说,有递归不可比拟的优势,这也是动态规划算法的核心之处

确定了动态规划的这三要素,整个求解过程就可以用一个最优决策表来描述,最优决策表是一个二维表,其中行表示决策的阶段,列表示问题状态,表格需要填写的数据一般对应此问题的在某个阶段某个状态下的最优值(如最短路径,最长公共子序列,最大价值等),填表的过程就是根据递推关系,从1行1列开始,以行或者列优先的顺序,依次填写表格,最后根据整个表格的数据通过简单的取舍或者运算求得问题的最优解。

动态规划法的经典示例

示例一:上台阶有多少种走法?
问题描述:有一个n阶的台阶,一个小孩每次可以走1步或者两步,问共有多少种上台阶的走法?

public int step(int n) {    if(n == 1)        return 1;    if(n == 2)        return 2;    //用一个数组保存子问题的解    int[] num = new int[n+1];    num[1] = 1;    num[2] = 2;    for(int i = 3; i <= n; i++) {        //体现了子问题的相互依赖关系        num[i] = num[i-1] + num[i-2];    }    return num[n];}

示例二:0-1背包问题
问题描述:给定n种物品和一个背包,物品i的重量为wi,其价值为vi,背包的容量为C。问应如何选择装入背包的物品,使得装入背包中物品的总价值最大?

public int maxValue(int C, int[] w, int[] v, int n) {        if(C < 1 || n < 1)            return 0;        /**         * m[i[j]表示背包容量为j,可选物品为i,i+1,...,n时0-1背包的最优值。         */        int[][] m = new int[n+1][C+1];        for(int i = 1; i <= C; i++) {            if(w[n-1] >= i) {                m[n][i] = v[n-1];            }else {                m[n][i] = 0;            }        }        for(int i = n -1; i > 0; i++) {            for(int j = 1; j <= C; j++) {               if(w[i-1] >= j) {                   m[i][j] = Math.max(m[i+1][j], m[i+1][j-w[i-1]] + v[i-1]);               } else {                   m[i][j] = m[i+1][j];                }            }        }        return m[1][C];    }

示例三:最大子序列和。
问题描述:有一个整型的数组,求连续子序列和的最大值。
例如:序列1,-3,5,2,-1,2的最大子序列和为8。

public int maxSequence(int[] a, int n) {    if(n < 1)        return 0;    int[] maxH = new int[n];    //初始化    maxH[0] = a[0];    int res = maxH[0];    for(int i= 1; i<n; i++) {        //子问题的依赖性        maxH[i] = a[i] + maxH[i-1] > 0?maxH[i-1]:0;        res = Math.max(res, maxH[i]);    }    return res;}

总结:动态规划法在算法中用的太多了,通常其并没有统一的刻画公式,因此,在一些题中很难想到符合动态规划法的模式。也就是刻画其结构特征比较困难。各位一起努力吧!!