动态规划-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;
}
  • 14
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: Spine-Unity-3.8-2021是一个Spine动画的Unity插件,可以让开发人员在游戏开发过程中轻松实现Spine动画。这个插件包含了最新的Spine Runtime库,可以让开发人员在使用Spine动画时获得更好的性能和可靠性。此版本还提供了对C# 9的支持,并修复了在以前的Spine Unity版本中存在的一些问题和异常。此外,此版本也提供了一些改进和优化,能够让开发人员更快速、更容易地实现动画效果并提高游戏的运行效率。总的来说,Spine-Unity-3.8-2021是一个值得开发人员关注和使用的插件,它可以帮助游戏开发人员更好地实现动画效果,提升游戏的质量和用户体验。 ### 回答2: spine-unity-3.8-2021是一款在Unity中使用的Spine动画软件包。Spine是一种2D骨骼动画软件,它可以帮助制作2D游戏中的角色动画。与传统的逐帧动画相比,Spine骨骼动画具有更高的效率和更好的表现效果。 spine-unity-3.8-2021是Spine运行在Unity中的版本。它提供了一个简便的方式来将Spine制作的动画集成到Unity游戏中。spine-unity-3.8-2021拥有许多实用的功能,例如动画的播放、循环、暂停和停止等。 此外,spine-unity-3.8-2021还支持动画的混合、遮罩、缩放等高级特性。这些功能可以大大提升2D游戏的动画表现效果。 总之,spine-unity-3.8-2021是一款强大的Spine骨骼动画软件包,它可以帮助Unity开发者更方便、更高效地制作2D游戏中的动画效果。 ### 回答3: Spine-Unity-3.8-2021是Spine动画引擎的一个版本,其主要特点是可以与Unity引擎无缝集成,提供了高效、灵活、可定制的动画解决方案。此版本相比之前版本,主要增加了一些新功能和改进,如支持GPU动画混合、2D环境的自适应、高效的顶点色边框渲染、Spine Atlas纹理集加载、支持Mecanim(动画过渡和状态机和蒙太奇)和 IK姿势、环境光遮蔽、大量优化和 bug 修复等。除此之外,它还易于使用和实现,并具有快速迭代的能力,可以让开发者轻松创建精美的动画效果,提升游戏的用户体验。该版本是Spine引擎的主要升级版本之一,同时也体现出Spine开发团队对于产品需求和用户反馈的重视和努力,不仅提高了动画制作领域的生产力和创造力,也为游戏行业推陈出新提供了有力支持。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

SL_World

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

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

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

打赏作者

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

抵扣说明:

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

余额充值