动态规划-3.8流水作业调度

问题描述:

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

分析:

直观上,一个最优调度应使机器M1没有空闲时间,且机器M2空闲时间最少。一般情况下,机器M2上会有机器空闲和作业积压2种情况。

设全部作业的集合为N={1,2,…,n}。SN是N的作业子集。在一般情况下,机器M1开始加工S中作业时,机器M2还在加工其他作业,要等时间t后才可利用。将这种情况下完成S中作业所需的最短时间记为T(S,t)。流水作业调度问题的最优值为T(N,0)。

其中a[i]是第i个作业在M1上工作的时间,b[i]是第i个作业在M2上工作的时间,同一作业分别在两个机器上的工作时间不一定相同

1
针对这两个递归等式,如果觉得不易理解,请看如下图示。
在这里插入图片描述

流水作业调度思想三大步骤:

3

流水作业调度程序实现四大步骤

①令N1={i|a[i]< b[i]},N2={i|a[i]>=b[i]}
②按key值升序排序
③连接N1&N2
④计算总时间

例如:*表示选择某作业时该选择比较的key值

作业J0J1J2J3J4J5
M130*12050*20*90110
M280100*906030*10*

N1 = {J3,J0,J2}(升序);N2 = {J1,J4,J5}(降序)

public class test3_8 {
	public static int flowShop(int[] M1,int[] M2,int[] c){
		int n = M1.length;
		Element[] d = new Element[n]; //作业元素
		//1.令N1={i|a[i]<b[i]},N2={i|a[i]>=b[i]}
		for(int i=0;i<n;i++){
			int key = (M1[i]<M2[i])? M1[i]:M2[i];  
			int index = i;
			boolean job = (M1[i]<=M2[i]);  //job=1表示N1,job=0表示N2
			d[i] = new Element(key,index,job);
		}
		//2.按key值升序排序
		bobleSort(d);
		//3.连接N1&N2
		int count=0;
		int k = n-1;
		for(int i=0;i<n;i++){
			if(d[i].job)  c[count++] = d[i].index;    //如果是N1
			else c[k--] = d[i].index;                 //如果是N2
		}
		//4.计算总时间
		int time1 = M1[c[0]];
		int time2 = time1+M2[c[0]];
		for(int i=1;i<n;i++){
			time1 += M1[c[i]];
			//此处不能是 += 
			time2 = (time1<time2)?(time2+M2[c[i]]):(time1+M2[c[i]]);
		}
		return time2;
	}
	//整个数组无需分N1或N2排序,整体升序排序也保证N1&N2内部为升序排序
	public static void bobleSort(Element[] d){
		int n = d.length;
		Element temp;
		for(int i=0;i<n-1;i++){
			boolean ok = true;
			for(int j=1;j<n-i;j++){
				if(d[j-1].key>d[j].key){
					temp = d[j];
					d[j] = d[j-1];
					d[j-1] = temp;
					ok = false;
				}
			}
			if(ok) break;
		}
	}
	public static void main(String[] args) {
		int n = 6;
		int time;  //最优作业调度花费总时间
		int[] c = new int[n];  //作业调度顺序
		int[] M1 = {30,120,50,20,90,110}; //在M1机器上的工作时间
		int[] M2 = {80,100,90,60,30,10}; //在M2机器上的工作时间
		System.out.print("M1机器上顺序工作时间为:");
		for(int i=0;i<n;i++) System.out.print("("+i+")"+M1[i]+" ");
		System.out.println();
		System.out.print("M2机器上顺序工作时间为:");
		for(int i=0;i<n;i++) System.out.print("("+i+")"+M2[i]+" ");
		System.out.println();
		time = flowShop(M1,M2,c);
		System.out.println("完成作业最短时间为:"+time);
		System.out.print("作业最优调度顺序为:");
		for(int i=0;i<n;i++)
			System.out.print(c[i]+" ");
		System.out.println();
	}
}
//作业元素(key,index,job)
class Element{
	int key;   //排序关键码
	int index;  //作业序号
	boolean job;  //N1&N2标识符
	public Element(int k,int i,boolean j){
		this.key = k;
		this.index = i;
		this.job = j;
	}
}

运行结果如下:

M1机器上顺序工作时间为:(0)30 (1)120 (2)50 (3)20 (4)90 (5)110 
M2机器上顺序工作时间为:(0)80 (1)100 (2)90 (3)60 (4)30 (5)10 
完成作业最短时间为:430
作业最优调度顺序为:3 0 2 1 4 5 

补充:以上代码用的排序算法是冒泡排序,大家可以更换成快速排序,这样可以使本算法时间复杂度降到最低,即O(nlogn),因为时间复杂度最高的就是排序代码段,除排序以外代码段都是一个for循环,该算法所需空间为O(n)

附C++代码

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
typedef struct JobUnit{
    long T1;  // 作业在第1个机器上的时间
    long T2;  // 作业在第2个机器上的时间
};
class Solution{
    static bool cmp(JobUnit a, JobUnit b){
        bool aIsP = a.T1 < a.T2; // 若作业a在第1台机器的耗时小于在第2台机器的耗时,则用小者做比较
        bool bIsP = b.T1 < b.T2;
        if(aIsP && bIsP)         // 若两个作业的key值都是第1台机器的耗时,则按升序排序
            return a.T1 < b.T1;
        else if(!aIsP && !bIsP)  // 若两个作业的key值都是第2台机器的耗时,则按降序排序
            return a.T2 > b.T2;
        else if(aIsP && !bIsP)   // 若第1个作业的key值是第1台机器,第2个作业的key值时第2台机器,则不交换
            return true;
        return false;       // 若第1个作业的key值是第2台机器,第2个作业的key值时第1台机器,则交换位置
    }
public:
    long jobSchedule(vector<JobUnit>& job, int n){
        // 1.划分N1和N2并按不同key值排好序
        sort(job.begin(), job.end(), cmp);
        // 2.计算最低总耗时
        for(int i = 0;i < n;i++){
            minTime += job[i].T1;
            if(i > 0)
                // 1)之前所有任务在第二台机子的累计剩余、2)前一个任务在第二台机子上的剩余和3)当前任务在第二台机子上的耗时取最大
                remainTime = max(remainTime - job[i].T1, max((job[i-1].T2 - job[i].T1), job[i].T2));
            else
                remainTime = job[i].T2;
        }
        return minTime + remainTime;
    }
private:
    long minTime = 0, remainTime;
};
int main(){
    int n;
    JobUnit jobunit;
    vector<JobUnit> job;
    while(cin.peek() != '\n'){
        scanf("%ld", &jobunit.T1);
        job.push_back(jobunit);
    }
    n = job.size();
    for(int i = 0;i < n;i++)
        scanf("%ld", &job[i].T2);
    Solution sol;
    cout<<sol.jobSchedule(job, n)<<endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SL_World

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值