動態規劃

*基本思想**子問題不獨立(重疊子問題)*

*將待求解問題劃分成若干子問題,先求解子問題,然後從子問題中得到原問題的解。與分治法不同的是,動態規劃用一個表來記錄所有已解決的子問題的答案。

*動態規劃適用於最優化問題。
**要素:**
    最優子結構性質
    重疊子問題

步驟

(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);
    }


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