數據結構與算法學習筆記——動態規劃(1)

動態規劃問題一:裝配線調度問題

裝配線調度問題是動態規劃的經典問題:一個汽車底盤在進入每一條裝配線後,在裝配站中會在底盤安裝部件,然後完成裝配的汽車在裝配線末端離開。每條裝配線有n個裝配站,編號爲j=1,2,...,n. 。將裝配線ii 爲1或2)的第j 個裝配站表示爲Si,j 。將裝配線1的 j 個站(S1,j )和裝配線2的 j 個站(S2,j )執行相同的功能。 但是這些站採用的技術不同,所以裝配時間不同。我們把在裝配站Si,j 上所需要的裝配時間記爲ai,j 。底盤從裝配線 i 的進入時間是 ei ,裝配完的汽車底盤離開裝配線 i 的時間爲xi
對於一些加急訂單,底盤仍然需要經過n個裝配站,但是爲了加快裝配速度,允許將部分完成的汽車在任何裝配站上從一條裝配線轉移到另外一條。我們把已經通過裝配站Si,j 的一個底盤從裝配線 i 移走的時間爲ti,j ,其中i=1,2,而j=1,2,3,……,n-1。問題是要確定在裝配線1 內選擇哪些站以及在裝配線2內選擇哪些站,以使裝配時間最短。問題描述如圖:
問題描述如圖

舉個具體的例子來說明並求解問題:
這裏寫圖片描述

問題劃分

動態規劃方法第一步是描述最優解的結構的特徵
此問題(找出通過裝配站Si,j 的最快線路)的最優解包含兩個子問題:找出通過S1,j 的最快線路;找出通過S2,j 的最快線路。
對於子問題一S1,j 的最快線路只能是以下二者之一:
(1)通過裝配站S1,j1 的最快路線,然後直接通過裝配站S1,j
(2)通過裝配站S2,j1 的最快路線,從裝配線2移動到裝配線1,然後通過S1,j

同理,通過裝配站S2,j 的最快線路也只能只以下二者之一:
(1)通過裝配站S2,j1 的最快路線,然後直接通過裝配站S2,j
(2)通過裝配站S1,j1 的最快路線,從裝配線2移動到裝配線1,然後通過S2,j

遞歸求解分析

動態規劃方法第二步是利用子問題的最優解來遞歸定義的一個最優值。
fi[j] 表示一個底盤從起點到裝配站Si,j 的最快可能時間。我們的最終目標是確定底盤通過工廠所以線路的最跨時間,記爲f 。底盤必須經由裝配線1或2通過裝配站n,最後到達工廠出口,即有:

f=min(f1[n]+x1,f2[n]+x2)

要對f1[1]f2[1] 進行推理也是比較容易的,
f1[1]=e1+a1,1
f2[1]=e2+a2,1
考慮計算fi[j] ,其中j=2,3,...,n,(i=1,2)
得到遞推公式:
f1[j]=e1+a1,1min(f1[j1]+a1,j,f2[j1]+t2,j1+a1,j),if j=1 if j>=2 

f2[j]=e2+a2,1min(f2[j1]+a2,j,f1[j1]+t1,j1+a2,j),if j=1 if j>=2 

f1[j] 的值就是子問題的最優解的值。

算法實現

計算最快時間和構造最快路線的C語言實現如下:

#include <stdio.h>
void main(){
    /*time consumption of assembly in every station of two saaembly lines( six stations in each line)*/
    int S[2][6]={{7,9,3,4,8,4},{8,5,6,4,5,7}};
    /*initialization time of each assemly line*/
    int e[2]={2,4}; 
    /*time cost of transfer parts from line 1 to line 2 and in the opposite direction*/
    int T[2][5]={{2,3,1,3,4},{2,1,2,2,1}};
    //time comsumption of delivery components out from the two lines
    int x[2]={3,2};
    //the least time comsumption from starting point to each station
    int f[2][6]={{0,0}};
    int L[2][5]={{0,0}};//record the moving path;
    int Path[6][2]={{0,0}};//the final path (station,line)
    int i=0,j=0,f_final=0,k11,k12,k21,k22,L_end;
    f[0][0]=e[0]+S[0][0];
    f[1][0]=e[1]+S[1][0];
    //stations on line 1
    for(i=1;i<6;i++){
        k11=f[0][i-1] + S[0][i]; //from one station to next on the same line 1
        k21=f[1][i-1] + S[1][i]; //from one station to next on the same line 2      
        k12=f[1][i-1] + T[1][i-1] + S[0][i];// from one station of line 2 to the one of line 1 
        k22=f[0][i-1] + T[0][i-1] + S[1][i];// from one station of line 1 to the one of line 2
        if(k11<k12){ //find the most time-saving road
            f[0][i]=k11;
            L[0][i-1]=1;
        }else{
            f[0][i]=k12;
            L[0][i-1]=2;
        }

        if(k21<k22){ //find the most time-saving road
            f[1][i]=k21;
            L[1][i-1]=2;
        }else{
            f[1][i]=k22;
            L[1][i-1]=1;
        }   
    }
    k11=f[0][5]+x[0];
    k22=f[1][5]+x[1];
    if(k11<=k22){
        f_final=k11;L_end=1;
    }else{
        f_final=k22;L_end=2;
    }

    //print the most efficient time consumption on every station
    printf("Time-consumption situation on every station of the two lines:\n");
    for(i=0;i<2;i++){
        printf("LINE %d: ",i);
        for(j=0;j<6;j++)
            printf("%3d",f[i][j]);
        printf("\n");
    }
    printf("It costs the most saving time: %d\n ",f_final);

    printf("The most time-saving path for every station:\n");
    for(i=0;i<2;i++){
        printf("LINE %d: ",i);
        for(j=0;j<5;j++)
            printf("%3d",L[i][j]);
        printf("\n");
    }
    //Obtain the optimal path
    Path[5][0]=6;
    Path[5][1]=L_end;
    for(i=4;i>=0;i--){
        Path[i][0]=i+1;
        Path[i][1]=L[Path[i+1][1]-1][i];        
    }
    printf("The most efficient road is:\n");
    for(i=0;i<6;i++){
        printf("station %d, line %d\n",i+1,Path[i][1]);
    }
}

運行結果如下:
這裏寫圖片描述

裝配線調度室動態規劃算法的一個經典問題。
動態規劃算法的設計主要有4步:

  1. 描述最優解的結果。
  2. 遞歸定義最優解的值。
  3. 按自底向上的方式計算最優解的值。
  4. 由計算出的結果構造一個最優解。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章