问题描述:

n个作业{0, 1, 2, …, n}在2台机器上M1 和M2 组成的流水线上完成加工。每个作业加工的顺序都是先在M1 上加工,后在M2 上加工。在两台机器上加工的时间分别为ai 和bi 。 确定这n个作业的加工顺序,使得从第一台作业开始加工,到最后一个作业完成加工所需要的时间最少。 

递归关系:

T(S, t) = min{ ai + T(S - {i}, bi) } 1  <= i <= n;

Johnson法则:

对于流水作业调度问题,必定存在一个最优调度π, 使得作业π(i)和π(i + 1)满足Johnson不等式 min{bπ(i), aπ(i + 1)} >= min{bπ(i + 1), aπ(i )}。

解题思路:

 方法1:

当min{ a1, a2,┅, an , b1, b2,┅, bn }=ak时,则对任何i≠k,都有min{bk, ai} ≥ min{bi,ak}成立,故此时应将作业k安排在最前面,作为最优调度的第一个执行的作业;
当min{ a1, a2,..., an , b1, b2, ..., bn }= bk时,则对任何i≠k,也都有min{bi,ak}≥min{bk,ai}成立,故此时应将作业k安排在最后面,作为最优调度的最后一个执行的作业。
n个作业中首先开工(或最后开工)的作业确定之后,对剩下的n-1个作业采用相同方法可再确定其中的一个作业,应作为n-1个作业中最先或最后执行的作业;反复使用这个方法直到最后只剩一个作业为止,最优调度就确定了 。
流程:
1. 将{a1,a2,…,an,b1,b2,…,bn}排成非递减序列;
2. 依次从序列中抽出最小元素m,如果m = aj且作业j还没有排入调度表,则把作业 j 安排在调度表可达的最左边一项空位上(设n个作业的调度表有n项,开始全部为空)。
3. 如果m = bj且作业j还没有排入调度表,则把作业j安排在调度表可达的最右边一项空位上。
4.如果作业j已排在调度表中,则取序列的下一个最小元素m,继续按上述方法调度,直到元素取完为止。
最后得到的调度表中的作业的顺序就是各作业的加工顺序。
例  设n = 4,
(a1,a2,a3,a4)=(3,4,8,10),
(b1,b2,b3,b4)=(6,2,9,15),
经排序后为
(b2,a1,a2,b1,a3,b3,a4,b4)=(2,3,4,6,8,9,10,15)。
设σ1,σ2,σ3,σ4是最优调度。

因为最小数是b2,故置σ4= 2。下一个次小的数是a1,置σ1= 1。接下去是a2,作业2已经被调度。再其次是b1作业1也已经被调度。下一个是a3,置σ2= 3,依次置σ3= 4。

代码:

/*

测试样例
输入:
4
3 6 4 2 8 9 10 10
输出
1 3 4 2

*/
#include <bits/stdc++.h>
using namespace std;
class NumType
{
public:
    int value;  //值
    int type;  //是a, 还是b
    int id;   //作业的id
public:
    NumType(){ }
    NumType(int v, int t, int i):value(v), type(t), id(i){ }
public:
    void SetValue(int v, int t, int i)
    {
        value = v, type = t, id = i;
    }
public:
    bool operator < (const NumType &a) const
    {
        return value < a.value;
    }
};
int n;
int p[1024]; //最优调度
int used[1024];  //作业i是否调度过
NumType N[1024];
void Input()
{
    int a, b;
    scanf("%d", &n);
    for(int i = 1, j = 0; i <= n; ++i)
    {
        scanf("%d %d", &a, &b);
        N[j++].SetValue(a, 0, i);
        N[j++].SetValue(b, 1, i);
    }
}
bool compare(const NumType &a, const NumType &b)
{
    return a < b;
}
void FlowShop()
{
    int endnum = n * 2;
    sort(N, N + 2 * n - 1);//sort(N, N + 2 * n - 1, compare);
    memset(used, 0, sizeof(used));
    memset(p, 0, sizeof(p));
    for(int i = 0, left = 1, right = n; i < endnum; ++i)
    {
        if(used[N[i].id] == 0)
        {
            if(N[i].type == 0)
            {
              p[left++] =  N[i].id;
              used[N[i].id] = 1;
            }
            else
            {
                p[right--] = N[i].id;
                used[N[i].id] = 1;
            }
        }
        else
            continue;
    }
}
void OutPut()
{
    for(int i = 1; i <= n; ++i)
    {
        cout << p[i] << " ";
    }
}
int main()
{
    Input();
    FlowShop();
    OutPut();

}

方法二:

流水作业调度问题的Johnson算法
(1)令N1 = { i | ai < bi}, N2 = { i | ai >= bi };
(2)将N1中作业依ai的非减序排序;将N2中作业依bi的非增序排序;

(3)N1中作业接N2中作业构成满足Johnson法则的最优调度。

流水调度问题-动态规划-Johnson法则-两种方法_动态规划

#include <bits/stdc++.h>
using namespace std;
class NumType
{
public:
    int value;
    int id;
public:
    NumType(){ }
    NumType(int v, int i):value(v), id(i){ }
public:
    void SetValue(int v, int i)
    {
        value = v, id = i;
    }
public:
//    bool operator < (const NumType &a) const
//    {
//        return value < a.value;
//    }
//    bool operator > (const NumType &a)const
//    {
//        return value > a.value;
//    }
};
int n, n_a, n_b;
int p[1024], a_v[1024], b_v[1024];
NumType A[1024]; // ai  < bi
NumType B[1024]; // ai >= bi
void Input()
{
    int a, b;
    n_a = n_b = 0;
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i)
    {
        scanf("%d %d", &a, &b);
        a_v[i] = a, b_v[i] = b;
        if(a < b)
        {
            A[n_a++].SetValue(a, i);
        }
        else
            B[n_b++].SetValue(b, i);
    }
}
bool compare_a(const NumType &a, const NumType &b)
{
    return a.value < b.value;
}
void FlowShop()
{
    memset(p, 0, sizeof(p));
    sort(A, A + n_a, compare_a);
    sort(B, B + n_b, compare_a);
//    for(int i = 0; i < n_a; ++i)
//        cout << A[i].id << " ";
//        cout << endl;
//     for(int i = 0; i < n_b; ++i)
//        cout << B[i].id << " ";
//        cout << endl;
    for(int i = 0; i < n_a; ++i)
        p[i + 1] = A[i].id;
    for(int i = 0; i < n_b; ++i)
        p[n - i] = B[i].id;
}
void OutPut()
{
    int time_a = a_v[p[1]];
    int time_b = time_a + b_v[p[1]];
    for(int i = 2; i <= n; ++i)
    {
        time_a += a_v[p[i]];
        time_b = time_a < time_b ? time_b + b_v[p[i]] : time_a + b_v[p[i]];
    }
    cout << "加工总时间为 : " << time_b << endl;
    cout << "加工顺序为: " << endl;
    for(int i = 1; i <= n; ++i)
    {
        cout << p[i] << " ";
    }
}
int main()
{
    Input();
    FlowShop();
    OutPut();
}
方法二运行截图:

流水调度问题-动态规划-Johnson法则-两种方法_#include_02