动态规划

*基本思想**子问题不独立(重叠子问题)*

*将待求解问题划分成若干子问题,先求解子问题,然后从子问题中得到原问题的解。与分治法不同的是,动态规划用一个表来记录所有已解决的子问题的答案。

*动态规划适用于最优化问题。
**要素:**
    最优子结构性质
    重叠子问题

步骤

(1)找出最优解的性质,刻画其结构特征(证明最优解具有最优子结构性质原问题具有最优解包含子问题的最优)

(2)递归的定义最优值。(构造最优的递归表达式)

(3)由自底向上的方法计算最优值

(4)根据最优值时得到的信息,构造最优值。(计算最优值)1. 矩阵连乘问题(**最优值为最少数乘次数)**
对于 p={30 35 155 10 20 25}:

这里写图片描述
这里写图片描述
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)建立递归关系

对于矩阵连乘积的最优计算次序问题,设计算A[i:j],1<=i<=j<=n,所需的最少数乘次数为m[i][j],则原问题的最优值为m[1][n], 
当i=j时,A[i:j]=Ai为单一矩阵,无需计算,因此m[i][j]=0,i=0,1,. 
n 当i<j 时,可利用最优子结构性质计算m[i][j]。事实上,若计算A[i:j]的最优次序在Ak和Ak+1之间断开,i<=k<j,则m[i,j]=min{m[i,k]+m[k+1,j] +pi-1*pk*pj,由于在计算的时候并不知道断开点k的位置,所以k还未确定,不过k的位置只有j-i种可能,即k属于{i,i+1,…….j-1},因此,k是这j-i个位置中使计算量达到最小的那个位置,从而,可递归定义。
![这里写图片描述](https://img-blog.csdn.net/20161112131019112)
m[i][j]给出了最优值,即计算A[i:j]所需要的最少数乘次数,同时确定了计算A[i:j]的最优次序中的断开位置k,也就是说对于k来说,有
   m[i,j]=min{m[i,k]+m[k+1,j] +pi-1*pk*pj   。
若将对于m[i][j]的断开位置k记为s[i][j],在计算出最优值m[i][j]后,可递归的由s[i][j]选出相应的最优解。

3)计算最优值
算法:
public static void matrixChain(int []p,int [][]m,int [][]s)
{
    //p储存维数,m为最优次,s为断点
    //matrixChain中,输入参数{p0p1..................pn}存储于数组p中。
     int n=p.length-1;
  for(int i=1;i<=n;i++)//单一矩阵都置为0
         m[i][j]=0;
        for(int r=2;r<=n;r++)//r为连乘矩阵个数
         for(int i=1;i<=n-r+1;i++)
         //从开始到结束
         {
            int j=i+r-1;//最后一个序号,首序号+步长-1
            m[i][j]=m[i+1][j]+p[i-1]*p[i]*p[j];
            s[i][j]=i;//设A[i]为断点
            for(int k=i+1;k<j;j++)
            //分别设Ai+1...............Aj-1为断点,把中间矩阵都作为断点分析一遍。
            {
            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)构造最优解

  public static void traceback(int [][]s, int i,int j)
{    //递归的方式来把最少乘数的表达式输出

    if (i == j)
       return ;
       traceback(s,i,s[i][j]);//从i到j最佳断开位置
       traceback(s,s[i][j]+1,j);
         System.out.println("Multiply A"+i+","+s[i]      [j]+"and A"+s[i][j]+1)+","+j);

    }



2.最长公共子序列

 1.最长公共子序列的结构
 对x的所有的子序列,检查它是否也是Y的子序列,从而确定它是否是X和Y的公共子序列。并且检查过程中记录最长的公共子序列。X的所有子序列都检查过后即可求出X和Y的最长公共子序列。X的每个子序列都相应于下标{1,2,.............m}的子集,因此,共有二的m次方个不同子序列。
 设序列X={x1,x2...........xn}和Y={y1,y2...........yn}的最公共子序列为Z={z1,z2.......zn}则
 (1)若 xm=yn,则 zk=xm=yn,且Zk-1是Xm-1和Yn-1的最长公共子序列; 
(2) 若 xm≠yn且 zk≠xm ,则 Z是 Xm-1和 Y的最长公共子序列;
(3) 若 xm≠yn且 zk≠yn ,则 Z是 X和 Yn-1的最长公共子序列;
   其中Xm-1={x1,x2...........xn},Yn-1={y1,y2...........yn},Zk-1={z1,z2.......zn}。
  2.子问题的递归结构
由最长公共子序列问题的最优子结构性质可知,要找出Xm=和Yn=的最长公共子序列,可按如下方式递归的进行:
 ·当xm = yn时,找出Xm-1和Yn-1的最长公共子序列,然后在其尾部加上xm或yn,即可得到X和Y的一个最长公共子序列;
 ·当xm≠yn时,必须解两个子问题,即找出Xm-1和Y的一个最长公共子序列及X和Yn-1的一个最长公共子序列。这两个公共子序列中较长者即为X和Y的一个最长公共子序列。

   由此递归结构容易看到最长公共子序列问题具有子问题重叠性质。例如,在计算X和Y的最长公共子序列时,可能要计算出X和Yn-1以及Xm-1和Y的最长公共子序列。而这两个子问题都包含一个公共子问题,即计算Xm-1和Yn-1的最长公共子序列。

   与矩阵乘积最优计算次序问题类似,我们来建立子问题的最优值的递归关系。用c[i,j]记录序列Xi和Yj的最长公共子序列的长度,其中Xi={x1,x2...........xn},Yj=}y1,y2...........yn}。当i = 0或j = 0时,空序列是Xi和Yj的最长公共子序列,故c[i,j] = 0。其他情况下,可得递归关系如下所示

这里写图片描述

  3 计算最优值
  计算最长公共子序列长度的动态规划算法LcsLength以序列X={x1,x2...........xn}和Y={y1,y2...........yn}作为输入,输出两个数组c和b,其中,c[i][j]存储Xi和Yj的最长公共子序列的长度,b[i][j]记录c[i][j]的值由哪一个子问题的解得到的,在这个最长公共子序列时要得到,问题的最优值,即X和Y 的最长公共子序列的长度记录于c[m][n]中。
 public static int LcsLength(char []x,char []y,int [][]b)

   {
      int m=x.length-1;
      int n=y.length-1;
      int [][]c=new int [m+1][n+1];
      for(int i=1;i<=m;i++)
           c[i][0]=0;
       for(int i=1;i<=n;i++)
           c[0][i]=0;
       for(int i=1;i<=m;i++)
       for(int i=1;i<=n;j++) {
         //对应第一个性质  
         if(x[i]==y[j])
         {
         c[i][j]=c[i-1][j-1]+1;
         b[i][j]=1;
         }
         //对应第二个性质  
       else if(c[i-1][j]>c[i][j-1])
       {
       c[i][j]=c[i-1][j]+1;
         b[i][j]=2;
         }
         else
         //对应第三个性质  
         c[i][j]=c[i-1][j]
         b[i][j]=3;
       }
       }
       return c[m][n];
       }
  4.构造最长公共子序列

//采用递归实现

     public static void Lcs(int i,int  j,char []x,char[]y)
   {
      if(i==0||j==0)
          return ;
        if(b[i][j]==1)
        {
         Lcs(i-1,j-1,x,b);
         System.out.println(x[i]);
         }
         else if(b[i][j]==2)
            Lcs(i-1,j,x,b);
            else
               Lcs(i,j-1,x,b);
          }





  3.流水作业调度
    n个作业{1,2,.......n}要在两台机器M1和M2组成流水线上加工,每个作业加工的顺序都是先在M1上加工,然后在M2上加工,M1和M2的加工作业i所需要的时间分别是ai和bi,1<i<n,流水作业调度问题要求确定这n个作业的最优加工顺序,使得从第一个作业在机器M1上开始加工,到最后一个作业在M2上加工完成所需的完成时间最少。
    1.最优子结构性质
    设π是所给n个流水作业的一个最优调度,它所需的加工时间为 aπ(1)+T’。其中T’是在机器M2的等待时间为bπ(1)时,安排作业π(2),…,π(n)所需的时间。
  记S=N-{π(1)},则有T’=T(S,bπ(1))。
  证明:事实上,由T的定义知T’>=T(S,bπ(1))。若T’>T(S,bπ(1)),设π’是作业集S在机器M2的等待时间为bπ(1)情况下的一个最优调度。则π(1),π'(2),…,π'(n)是N的一个调度,且该调度所需的时间为aπ(1)+T(S,bπ(1))<aπ(1)+T’。这与π是N的最优调度矛盾。故T’<=T(S,bπ(1))。从而T’=T(S,bπ(1))。这就证明了流水作业调度问题具有最优子结构的性质。
  2.递归计算最优值
 由流水作业调度问题的最优子结构性质可知:

这里写图片描述

  3 流水作业调度的Johnson法则 
    设兀是作业集S在机器M2的等待时间为t时的任一最优调度。若在这个调度中,安排在最前面的两个作业分别是i 和j ,即π(1)=I,π(2)=j,

这里写图片描述

 如果作业i和j满足min{bi,aj} ≥min{bj,ai},则称作业i和j满足Johnson 不等式。如果作业i和j 不满足Johnson不等式,则交换作业i和j满足Johnson不等式.
 当作业i和j 满足Johnson 不等式 min{bi,aj} ≥min{bj,ai}时,有

这里写图片描述
这里写图片描述

 4.构造最优值
 流水作业调度问题的Johnson算法
 (1)令N1={i|ai<bi},N2={i|ai>=bi},
 (2)将N1中作业依照ai的递增排序,N2中作业依照bi的递减排序。
 (3)N1中作业接N2中作业构成Johnson法则的最高调度。
public static int flowShop(int []a,int []b,int []c)
{
     int n=a.length-1;
     Element []d=new Element[n];

     for(int i=0;i<n;i++)
     {
     int key=a[i]>b[i]?b[i]:a[i];
     boolean job=a[i]<=[i];
     d[i]=new Element(key,i,job);
     }
       MerageSort.mergeSort(d);
      int j=0;k=n-1;
for(int i=0;i<n;i++){
       if(d[i].job)
       c[j++]=d[i].index;
      else
         c[k--]=d[i].index;
         }
     j=a[c][0];
     k=j+b[c[0]];
     for(int i=1;i<n;i++)
     {
        j+=a[c[i]];
        k=j<k? k+b[c[i]]:j+b[c[i]];
        }
        return k;

     }
    /************ Element*******************8/
     public static class Element implements Compareable
     {
        int key;
        int index;
        boolean job;
private Elemment(int kk,int ii,boolean jj)
{
  key=kk;
  index=ii;
  job=jj;
  }
//根据key进行排序
public int CompareTo  (Object)
{
  int xkey=((Element) x).key;
  if(key<xkey)
      return -1;
   if(key==xkey)  return 0;
    return 1;
    }
 }

0-1揹包问题
0-1揹包问题,给定n种物品和一揹包。物品i的重量是w[i],其价值为v[i],揹包的容量为C。问应如何选择装入揹包的物品,使得装入揹包中物品的总价值最大?
分析:对于一种物品,要么装入揹包,要么不装。所以对于一种物品的装入状态可以取0和1。设物品i的装入状态为xi,xi∈ (0,1),此问题称为0-1揹包问题。
0-1问题是一个特殊的整数划分问题。

   1。最优子结构性质:
      0-1揹包问题具有最优子结构性质,设(y1,y2,y3............yn)是所给的0-1问题的一个最优解,

这里写图片描述
2。递归关系
设所给0-1问题的子问题,这里写图片描述

的最优值为m(i,j),即m(i,j)是揹包容j,可选择物品为1,1+1………..n。式0-1问题的最优解,由0-1问题的最优子结构性质,可以建立以下计算m(i,j)的递归式:
这里写图片描述

3。算法描述;
   当w[i](1=<i<=n)为正整数时,用二维数组m[][]存储m(i,j)的相应位置。
//计算最优值
public static void knapsack(int []v,int[] w,int c,int[][]m)
{

     int n=v.length-1;
     int jMax=Math.min(w[n]-2,c);
     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--)
{
     jMax=Math.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]=Math.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]=Math.max(m[1][c],m[2][j-w[1]+v[1]);
         }

//构造最优解
 public staric void traceback(int [][]m,int []w,int c,int[]v)
 {
     int n=w.length-1;
     for(int i=1;i<n;i++)
     {
        if(m[i][c]==m[i+1][c])
             x[i]=0;
             else 
                x[i]=1;
                    c-=w[i];
    x[n]=(m[n][c]>0?1:0);
    }


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章