算法設計與分析——流水作業調度(動態規划)


 

一、問題描述

N個作業{1,2,………,n}要在由兩台機器M1和M2組成的流水線上完成加工。每個作業加工的順序都是先在M1上加工,然后在M2上加工。M1和M2加工作業i所需的時間分別為ai和bi,1≤i≤n。流水作業高度問題要求確定這n個作業的最優加工順序,使得從第一個作業在機器M1上開始加工,到最后一個作業在機器M2上加工完成所需的時間最少。

二、算法思路

直觀上,一個最優調度應使機器M1沒有空閑時間,且機器M2的空閑時間最少。在一般情況下,機器M2上會有機器空閑和作業積壓2種情況。

最優調度應該是:

1. 使M1上的加工是無間斷的。即M1上的加工時間是所有ai之和,但M2上不一定是bi之和。

2. 使作業在兩台機器上的加工次序是完全相同的。

則得結論:僅需考慮在兩台機上加工次序完全相同的調度。

設全部作業的集合為N={1,2,…,n}。S是N的作業子集。在一般情況下,機器M1開始加工S中作業時,機器M2還在加工其他作業,要等時間t后才可利用。將這種情況下完成S中作業所需的最短時間記為T(S,t)。流水作業調度問題的最優值為T(N,0)。   

這個T(S,t)該如何理解?舉個例子就好搞了(用ipad pencil寫的...沒貼類紙膜,太滑,湊合看吧)

1、最優子結構

T(N,0)=min{ai + T(N-{i}, bi)},  i∈N。

ai:選一個作業i先加工,在M1的加工時間。

T(N-{i},bi}:剩下的作業要等bi時間后才能在M2上加工。注意這里函數的定義,因為一開始工作i是隨機取的,M1加工完了ai之后,要開始加工bi了,這里M1是空閑的可以開始加工剩下的N-i個作業了,但此時M2開始加工bi,所以要等bi時間之后才能重新利用,對應到上面函數T(s,t)的定義的話,這里就應該表示成T(N-{i},bi), 所以最優解可表示為T(N,0)=min{ai + T(N-{i}, bi)},  i∈N,即我們要枚舉所有的工作i,使這個式子取到最小值。

繼續分析T(S,t)可得:

   T(S,t)={ai + T(S-{i}, bi+max{t-ai,0})}, i∈S

其中:T(S-{i}, bi+max{t-ai,0}):剩下的作業等bi+max{t-ai,0}才能在M2加工,至於這里是怎么推導出來的呢?見下面推導:

2、最優子結構性質

 

這段證明不是很好理解,簡單來說就是要證明問題的最優解包含子問題的最優解就行了,那么這里的證明思路是先假設一個最優調度,對於他的子調度T’,因為T(S,t)被定義為是完成S中作業所需的最短時間記為T(S,t),所以有T’>=T(S, bπ1),那么如果這個子調度這里不是最優解的話即T’>T(S, bπ1),會得出aπ1+T’ > aπ1+T(S, bπ1)即原來假設的最優調度不符和最優調度的標准,矛盾,從而推出 T’是一定等於T(S, bπ1),即這個子調度也是最優調度。

問題是雖然滿足最優子結構性質,也在一定程度滿足子問題重疊性質。N的每個非空子集都計算一次,共2n-1次,指數級的。https://images2015.cnblogs.com/blog/1037219/201611/1037219-20161107104308389-1375519089.png

為了解決這個問題引入Johnson不等式

3、Johnson不等式

 

 推導公式的最后兩步,作用是提出bi和aj,然后直接max三元素

 

 

4、算法描述

 

假設有下列的7個作業:

推測一下這個Johson法則為什么能夠得到最小的作業時間?

Johson法則分出的第一組都是M2加工時間大於M1的,且按M1時間遞增;分出的第二組都是M1加工時間大於M2的,且按M2時間遞減。

由於M1加工是無間斷的,決定時間長短的只是M2。按照Johson法則會發現,中間部分都是一些M2耗時大的作業,兩頭都是一些耗時小的作業,個人覺得這樣安排會很好填充M2中的時間空隙。

5、代碼演示

#include <iostream>
#include <algorithm>
using namespace std;
class JOB
{
public:
    int key,index;
    bool job;
};
bool cmp(JOB a,JOB b)
{
    return a.key<b.key;
}
int func(int n,int a[],int b[],int c[])
{
    int i,j,k;
    JOB *d =new JOB[n];
    for(i=0;i<n;i++)
    {
        if(a[i]<b[i])
        {

            d[i].job =true;
            d[i].key =a[i];
        }
        else
        {
            d[i].job=false;
            d[i].key=b[i];
        }
        d[i].index=i;
    }
    sort(d,n+d,cmp);
    j=0,k=n-1;
    for(i=0;i<n;i++)
    {
        if(d[i].job ==true)
            c[j++]=d[i].index;
        else
            c[k--]=d[i].index;
    }
    j=a[c[0]];
    k=j+b[c[0]];
    for(i=1;i<n;i++)
    {
    j=j+a[c[i]];
    k= j<k ? k+b[c[i]] : j+b[c[i]] ;
    }
    delete d;
    return k;
}
int main()
{
    int i,n,m,a[100],b[100],c[100];
    cin>>n;
    while(n--)
    {
        cin>>m;
        for(i=0;i<m;i++)
        {
            cin>>a[i];
            cin>>b[i];
        }
        cout<<func(m,a,b,c)<<endl;
    }
    return 0;
}
/*
1
7
5 2
3 4
6 7
4 2
8 9
9 7
6 3
*/

結果:43


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM