算法——动态规划

来源:互联网 发布:如何购买淘宝客户资料 编辑:程序博客网 时间:2024/04/29 20:49

定义:

    动态规划过程是:每次决策依赖于当前状态,又随即引起状态的转移。一个决策序列就是在变化的状态中产生出来的,所以,这种多阶段最优化决策解决问题的过程就称为动态规划。

基本思想:

   动态规划的基本思想是把一个问题分解为若干个相同性质的子问题,这些子问题具有重叠性,即不相互独立,并且为了不重复计算这些重叠问题,动态规划在计算的过程中会把计算结果保存,但遇到同样的问题时只需查找该问题的结果,无需重新计算,避免了重复计算。我们用子问题的最优解来构造原问题的最优解。

适用情况:

最优子结构:如果一个问题的最优解包含其子问题的最优解,则称该问题具有最优子结构。

子问题重叠:子问题之间不相互独立,递归算法反复求解相同的子问题

无后效性:某决策状态以后的过程不会影响以前的状态,只与当前状态有关。

求解步骤:

  1. 刻画一个最优解的结构特征。
  2. 递归地定义最优解的值。
  3. 求解最优解的值,一般采用自底向上方法。
  4. 利用计算出的信息构造一个最优解。

最长公共子序列:

定义:

    给定序列X和Y,若存在序列Z,使得Z既是X的子序列,也是Y的子序列,且Z是两者最长的子序列,则称Z为最长公共子序列。一个字符串的子序列,是指从该字符串中去掉任意多个字符后剩下的字符在不改变顺序的情况下组成的新字符串。

求解步骤:

最长公共子序列问题是给定两个序列X和Y,求解X和Y长度最长的公共子序列。

步骤1:刻画最长公共子序列的特征

为两个序列,为X和Y的任意LCS。

  1. 若果,则的一个LCS。
  2. 如果,则意味着的一个LCS。
  3. 如果,则意味着的一个LCS。

步骤2:递归地定义最优解的值

    在求解的一个LCS时,我们必须求解一个或两个子问题。如果,我们应该求解的一个LCS。将追加在这个LCS的尾端,就得到X和Y的一个LCS。如果,我们必须求解两个子问题:求解的一个LCS与的一个LCS。两个LCS较长者即为X和Y的一个LCS。由于这些问题覆盖了所有的可能性,所以我们知道必然有一个子问题的最优解出现在X和Y的LCS中。

    我们很容易看出子问题的重叠性质。为了求解X和Y的一个LCS,子问题都包含了求解的LCS的子子问题。

    我们定义表示的LCS的长度。如果,即一个序列长度为0,那么LCS的长度就为0,根据LCS问题的最优子结构性质,可得到如下的递归公式:

步骤3:计算LCS的长度

    利用步骤2的递归式,我们将很容易就能写出一个计算c[i,j]的指数时间的递归算法。由于在所考虑的子问题空间中,总共只有θ(m*n)个不同的子问题,因此,用动态规划算法自底向上地计算最优值能提高算法的效率。

    计算最长公共子序列长度的动态规划算法LCS_LENGTH(X,Y)接受序列X=<x1, x2, …, xm>和Y=<y1, y2, …, yn>作为输入。将c[i,j]的值输出保存在两个数组c[0..m ,0..n]和b[1..m ,1..n]中。其中c[i,j]存储Xi与Yj的最长公共子序列的长度,b[i,j]记录指示c[i,j]的值是由哪一个子问题的解达到的,这在构造最长公共子序列时要用到。最后,X和Y的最长公共子序列的长度记录于c[m,n]中。

Procedure LCS_LENGTH(X,Y);  begin    m:=length[X];    n:=length[Y];    let b[1...m,1...n]and c[0...m,0...n]be new tables.  for i:=1 to m do c[i,0]:=0;    for j:=1 to n do c[0,j]:=0;    for i:=1 to m do      for j:=1 to n do        if x[i]=y[j] then          begin            c[i,j]:=c[i-1,j-1]+1;            b[i,j]:="↖";          end        else if c[i-1,j]≥c[i,j-1] then          begin            c[i,j]:=c[i-1,j];            b[i,j]:="↑";          end        else          begin            c[i,j]:=c[i,j-1];            b[i,j]:="←"          end;    return(c,b);  end;   

    由算法LCS_LENGTH计算得到的数组b可用于快速构造序列的最长公共子序列。首先从b[m,n]开始,沿着其中的箭头所指的方向在数组b中搜索。

  • 当b[i,j]中遇到"↖"时(意味着是LCS的一个元素),表示的最长公共子序列是由Xi-1与Yj-1的最长公共子序列在尾部加上xi得到的子序列;
  • 当b[i,j]中遇到"↑"时,表示Xi与Yj的最长公共子序列和Xi-1与Yj的最长公共子序列相同;
  • 当b[i,j]中遇到"←"时,表示Xi与Yj的最长公共子序列和Xi与Yj-1的最长公共子序列相同。

    这种方法是按照反序来找LCS的每一个元素的。由于每个数组单元的计算耗费Ο(1)时间,算法LCS_LENGTH耗时Ο(mn)。

    下图显示了LCS_LENGTH对输入X=<A,B,C,B,D,A,B>和Y=<B,D,C,A,B,A>的生成的结果。图中计算出的表b和表c,第i行和第j列的方格包含了c[i,j]的值和b[i,j]记录的箭头。图中右下角表项c[7,6]中的4即为X和Y的一个LCS<B,C,B,A>的长度。


步骤4:构造LCS的一个最优解

    我们可以用LCS_LENGTH计算得到的数组b可用于快速构造序列的最长公共子序列。只需简单地从b[m,n]开始,沿着其中的箭头所指的方向在数组b中搜索。当b[i,j]中遇到"↖"时(意味着是LCS的一个元素),表示的最长公共子序列是由Xi-1与Yj-1的最长公共子序列在尾部加上xi得到的子序列。

Procedure PRINT_LCS(b,X,i,j);  begin    if i==0 or j==0      then return;    if b[i,j]="↖" then      begin        PRINT_LCS(b,X,i-1,j-1);        print(x[i]);     end    else if b[i,j]="↑"            then PRINT_LCS(b,X,i-1,j)            else PRINT_LCS(b,X,i,j-1);  end;   


参考

http://blog.csdn.net/v_july_v/article/details/6695482

http://www.cnblogs.com/steven_oyj/archive/2010/05/22/1741374.html

0 0
原创粉丝点击