问题描述:
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法则的最优调度。
#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();
}
方法二运行截图:
标签:bi,法则,Johnson,int,作业,调度,value,NumType
From: https://blog.51cto.com/u_16129621/6349657