第3章学习小结

动态规划 —— 经分解得到的子问题往往不是互相独立的

一、基本思想:保存已解决的子问题的答案,在需要时再找出已求得的答案,这样可以避免大量的重复计算,从而得到多项式时间算法。为了达到此目的,可以用一个表来记录所有已解        决的子问题的答案,不管该子问题以后是否被用到,只要它被计算过就将其结果填入表中。

       1、设计步骤:①找出最优解的性质并刻划其结构特征  ②递归的定义最优值   

                              ③以自底向上的方式计算最优值              ④根据计算最优值时得到的信息,构造最优解

二、矩阵连乘问题

       1、分析最优解结构            

            计算 A[1:n] 的最优次序所包含的计算矩阵子链 A[1:k] 和 A[k+1:n] 的次序也是最优的。事实上,如果计算 A[1:k] 的次序需要的计算量更少,则用此次序替换原来计算 A[1:k] 的次序,得到的计算 A[1:n] 的计算              量将比最优次序所需计算量更少,这是一个矛盾。同理可知,计算 A[1:n] 的最优次序包含的计算矩阵子链 A[k+1:n] 的次序也是最优的。因此,矩阵连乘积计算次序问题的最优解包含着其子问题的最优解。这种            性质称为最优子结构性质。问题的最优子结构性质是该问题可用动态规划算法求解的显著特征。

        2、建立递归关系

             m[i][j]=0         i=j       /    min{m[i][k] + m[k+1][j] + Pi-1 Pk Pj         i<j     (其中 i ≤ k ≤ j)

            若将对应的 m[i][j] 的断开位置 k 记为 s[i][j] ,在计算出最优值 m[i][j] 后,可递归地由 s[i][j] 构造出相应的最优解

        3、计算最优值

 

void MatrixChain(int *p,int n,int **m,int **s)
{
    for(int i=1;i<=n;i++)
        m[i][j]=0;
    for(int r=2;r<=n;r++)
    {
        for(int i=1;i<=n-r+1;i++)
        {
            int j=i+r-1;
            m[i][j]=m[i+1][j]+p[i-1]*p[i]*p[j];
            s[i][j]=i;
            for(int k=i+1;k<j;k++)
            {
                int t=m[i][k]+m[k+1][j]+p[i-1]*p[k]*p[j];
                if(t<m[i][j])
                {
                    m[i][j]=t;
                    s[i][j]=k;
                }
            }
        }
    }
} 

 

       4、构造最优解

三、动态规划算法的基本要素

       1、最优子结构。当问题的最优解包含了其子问题的最优解时,称该问题具有最优子结构性质。在动态规划算法中,利用问题的最优子结构性质,以自底向上的方式,递归地从子问题的最优解逐步构造出整             个问题的最优解。  

      2、重叠子问题。在用递归算法自顶向下解此问题时,每次产生的子问题并不总是新问题,有些子问题被反复计算。动态规划算法利用了这种子问题的重叠性质,对每个子问题只解一次,然后将其解保存在一个表            格中,当再次需要解此子问题时,只是简单地用常数时间查看一下结果。通常,不同的子问题个数随问题的大小呈多项式增长。因此,用动态规划算法通常只需要多项式时间,从而获得较高的解题效率。

四、最长公共子序列

       用 c[i][j] 记录序列 XiYi 的最长公共子序列长度

                     0                                      i>0; j=0

       c[i][j]=    c[i-1][j-1] + 1                     i,j>0; xi = yi

                     max {c[i][j-1],c[i-1][j]         i,j>0; xi ≠ yi

void LCSLength(int m,int n,char *x,char *y,int **c,int **b)
{
    int i,j;
    for(i=1;i<=m;i++)
        c[i][0]=0;
    for(i=1;i<=n;i++)
        c[0][i]=0;
    for(i=1;i<=m;i++)
    {
        for(j=1;j<=n;++j)
        {
            if(x[i]==y[j])
            {
                c[i][j]=c[i-1][j-1]+1;
            }
            else if(c[i-1][j]>=c[i][j-1])
            {
                c[i][j]=c[i-1][j];
            }
            else
            {
                c[i][j]=c[i][j-1];
            }
        }
    }
}

 

五、0-1 背包问题

maxΣ vi xi  {    Σ wi xi ≤ c    

                       xi ∈ {0,1}      1 ≤ i ≤ n

 0-1背包问题的子问题的最优值为m(i,j),即m(i,j)是背包容量为j,可选择物品为i,i+1,……,n时,0-1背包问题的最优值。原始问题m(1,c)

递归式  m(i,j)=        max{m(i + 1, j),  m(i + 1, j - wj) + vi}    j≤wi            ( 第 i 个物品可以放得下,权衡放不放)

                           m(i + 1 , j)                                          0≤ j ≤ wn      (第 i 个物品放不下,wi > j )

                            

             m(n,j)=vn       j ≥ wn    /     0    0 ≤ j ≤ wn   

 

void knapsack(Type v,int w,int c,int n,Type **m)
{//自底向上填表 
    int jMax=min(w[n]-1,c);
    //第n行 
    for(int j=0;j<=jMax;j++) //放不下 
        m[n][j]=0;
    for(int j=w[n];j<=c;j++) //开始放得下 
        m[n][j]=v[n];
    for(int i=n-1;i>1;--i)   //第2~n-1行,第i行:当前物品
    {
        jMax=min(w[i]-1,c);
        for(int j=0;j<=jMax;j++) //放不下    
            m[i][j]=m[i+1][j];    
        for(int j=w[i];j<=c;j++) //
            m[i][j]=max(m[i+1][j],m[i+1][j-w[i]]+v[i]);
    } 
    m[1][c]=m[2][c];
    if(c>=w[1])
        m[1][c]=max(m[1][c],m[2][c-w[1]]+v[1]);
}

 

posted @ 2019-11-03 23:06  小蔡不能菜  阅读(125)  评论(0编辑  收藏  举报