模拟——作业调度方案最全解释

原题:
我们现在要利用m台机器加工n个工件,每个工件都有m道工序,每道工序都在不同的指定的机器上完成。每个工件的每道工序都有指定的加工时间。 每个工件的每个工序称为一个操作,我们用记号j-k表示一个操作,其中j为1到n中的某个数字,为工件号;k为1到m中的某个数字,为工序号,例如2-4表示第2个工件第4道工序的这个操作。在本题中,我们还给定对于各操作的一个安排顺序。 例如,当n=3,m=2时,“1-1,1-2,2-1,3-1,3-2,2-2”就是一个给定的安排顺序,即先安排第1个工件的第1个工序,再安排第1个工件的第2个工序,然后再安排第2个工件的第1个工序,等等。 一方面,每个操作的安排都要满足以下的两个约束条件。 (1) 对同一个工件,每道工序必须在它前面的工序完成后才能开始; (2) 同一时刻每一台机器至多只能加工一个工件。 另一方面,在安排后面的操作时,不能改动前面已安排的操作的工作状态。 由于同一工件都是按工序的顺序安排的,因此,只按原顺序给出工件号,仍可得到同样的安排顺序,于是,在输入数据中,我们将这个安排顺序简写为“1 1 2 3 3 2”。 还要注意,“安排顺序”只要求按照给定的顺序安排每个操作。不一定是各机器上的实际操作顺序。在具体实施时,有可能排在后面的某个操作比前面的某个操作先完成。 例如,取n=3,m=2,已知数据如下: 则对于安排顺序“1 1 2 3 3 2”,下图中的两个实施方案都是正确的。但所需要的总时间分别是10与12。   当一个操作插入到某台机器的某个空档时(机器上最后的尚未安排操作的部分也可以看作一个空档),可以靠前插入,也可以靠后或居中插入。为了使问题简单一些,我们约定:在保证约束条件(1)(2)的条件下,尽量靠前插入。并且,我们还约定,如果有多个空档可以插入,就在保证约束条件(1)(2)的条件下,插入到最前面的一个空档。于是,在这些约定下,上例中的方案一是正确的,而方案二是不正确的。 显然,在这些约定下,对于给定的安排顺序,符合该安排顺序的实施方案是唯一的,请你计算出该方案完成全部任务所需的总时间。
在这里插入图片描述

输入
第1行为两个正整数m和n(其中m(<20)表示机器数,n(<20)表示工件数),用一个空格隔开:
第2行:个用空格隔开的数,为给定的安排顺序。
接下来的2n行,每行都是用空格隔开的m个正整数,每个数不超过20。
其中前n行依次表示每个工件的每个工序所使用的机器号,第1个数为第1个工序的机器号,第2个数为第2个工序机器号,等等。
后n行依次表示每个工件的每个工序的加工时间。
输出
只有一个正整数,为最少的加工时间。
样例输入
2 3
1 1 2 3 3 2
1 2
1 2
2 1
3 2
2 5
2 4
样例输出
10

我就想大概题目都没有人看完是吧,甚至看完了题目之后是不是感觉还没有理解,我觉得有个完整的题目和配图已经不错了,毕竟当时个人在某个编程网站上看到的题目残缺不全,甚至看了好久也没有看明白题目。

首先,我们就来理解一下题目:

  • 就是有两台不同的机器a,b(题目里只有两台机器,为了更好理解就具体表示出来).
  • 两台机器,每台机器同一时间只能做一个工序。
  • 文章里有3个工件,每个工件有2道工序(姑且叫做1,2),有2台机器
  • 还要注意,“安排顺序”只要求按照给定的顺序安排每个操作。不一定是各机器上的实际操作顺序。这句话什么意思? 意思就是工件局部是必须有序的,但是对于全部的工价它是不一定有序的,就如题目中提供的 1-1,1-2,2-1,3-1,3-2,2-2你看他的这个次序,我做完2-1,那么我必须先做3-1吗? 不是,我做完2-1,我可以去做2-2,不一定要做2-1后面的3-1,这就是不一定是各机器上的实际操作的意思。那第二句什么意思?又说有序?有序其实就是工件局部必定是有序的,就是向上面的那个序列,你不可能先做2-2再做2-1,你不可能先做3-2再做3-1,但是这就很坑--------原题和输入数据的第三个工件的次序不一样,你看输入的次序是什么。
    1 2
    1 2
    2 1
    第一行是第一个工件,第二行是第二个工件,第三行是第三个工件,每个工件2道工序,你看第三个,第二道工序在前面。。。。。。。。。。(ctm)
    然后是对应的时间:
    3 2
    2 5
    2 4
    对应的分别是1-1,1-2,2-1,2-2,3-2,3-1。

理解完题目之后那就简单了,大概意思就是让两台机器尽可能的不违反两条前提工作嘛,万恶的资本主义。。。。。

做算法题,最主要的是理解数据与数据之间的关系,这样的题目完全可以通过数据结构来完成,因为数据结构本就是数据关系的集合,但是我们首先先用传统的方法来解决。

先理解数据之间的联系:
有三个工件,而且都有不同的工件编号(1,2,3);
每个工件都有两道工序(1,2),而且每道工序都有给定的时间(3 2 2 5 2 4)。

大概就是上面的关系了,所以我们需要三个容器来存储上面三个数据,一个是工件号(1 1 2 3 3 2),一个是工序号(1 2 1 2 2 1),最后一个是每道工序的时间(3 2 2 5 2 4)

然后还有两台(m)机器(m1,m2),每台机器都可以运行。

剩下的就是代码实现:

#include<stdio.h>
#include<string.h>
int step[199];//工件的工序,对应的是工件号
int num[22][22];//工件对应的机器
int time[22][22];//每道工序对应的时间,为0表示已经执行
int lasttime[22][22]={0};//工件每道工序最后完成的时间
int madecine[22][22]={0};//机器对应的运行时间
int main()
{
    int n,m;
    scanf("%d%d",&m,&n);
    int i,j;
    for(i=1;i<n*m;i++)
        scanf("%d",&step[i]);//存储的是工件号(1 1 2 3 3 2)
    for(i=1;i<=n;i++)//都从1开始,更好理解
        for(j=1;j<=m;j++)
            scanf("%d",&num[i][j]);//存储的是机器号(1 2 1 2 2 1)
    for(i=1;i<=n;i++)
        for(j=1;j<=m;j++)
            scanf("%d",&time[i][j]);//存储的是工序对应的时间(3 2 2 5 2 4)
    int s=0,fi=0,x;
    for(i=1;i<=n*m;i++)//共有m*n道工序
    {
        s=0;
        j=1;
        while(time[step[i]][j]==0) j++;//j都是从1开始的,但是这不一定是它的工序号,如第三个工件1对应的是2,2对应的是1
        int gj=step[i];
        for(x=lasttime[gj][j-1]+1;;x++)//last[][]初始化是为0,x最初为1,后续是某个工件某个工序完成的时间+1
        /*寻找机器可执行时间段*/
        {
            if(madecine[num[gj][j]][x]==0)//num[gj][j]对应的是机器号,意思就是每台机器对应的时间点是否已经用过了,没有用过则加的去
                s++;
            else s=0;//否则,在从x时间点开始道x+s这个时间段不足以满足当前工序所需要的时间
            if(s>=time[step[i]][j]) break;//找到即退出
        }
        lasttime[gj][j]=x;//找到一个符合的时间段x
        if(x>fi) fi=x;//记录最晚的时间
        while(time[gj][j]!=0) //将用了的时间段占值
        {
            madecine[num[gh][j]][x]=1;//x是当前机器对应的最后时间
            x--;//把前面的时间都置为1
            time[gj][j]--;//该工序时间减一,最后退出循环之时为0,表示该工序已经完成。
        }
    }
    printf("%d\n",fi);
    return 0;
}

用数据结构做原理相同:

#include<stdio.h>
struct node {
        int id;//在第id个机器上加工
        int cost;//花费的时间
    } w[21][21];//w[i][j]表示第i个物品,第j个工序
    int m,n;
    int order[500];//用来存储给定的做工顺序
    int step[21]= {0};//step[i]存储第i个物品当前进行到的工序号
    int last_time[21]= {0};//该物品上一个工序完成的时间
    int mac[21][100000]={0};//mac[i][j]表示第i个机器在时间点j是否空闲
    int ans=0;
int main() {
    scanf("%d%d",&m,&n);
    for(int i=1; i<=m*n; i++) {
        scanf("%d",&order[i]);
    }
    for(int i=1; i<=n; i++) {
        for(int j=1; j<=m; j++) {
            scanf("%d",&w[i][j].id);
        }
    }
    for(int i=1; i<=n; i++) {
        for(int j=1; j<=m; j++) {
            scanf("%d",&w[i][j].cost);
        }
    }
    //对order序列进行处理
    for(int i=1; i<=m*n; i++) {
        int now = order[i];//当前要处理的物品编号
        int k = step[now];//当前是第几个工序
        k++;
        step[now]=k;
        //找出now这个物品需要在哪个机器上加工,及其时间
        int id = w[now][k].id;
        int cost = w[now][k].cost;
        int s = 0;//能分配到的时间累计
        //在对应的机器上找空余的时间段
        for(int j=last_time[now]+1; ; j++) {
            if(mac[id][j]==0) {
                s++;//空余时间+1
            } else {
                s=0;//从0开始累计,因为我们需要的是一整段连续的空余时间,而不是断断续续的时间
            }
            if(s==cost) {//到目前为止分配的时间够用,那就用
                for(int p = j - cost + 1; p<=j; p++) {
                    mac[id][p] = 1;//表示被使用,给他一个标记
                }
                if(j>ans) ans=j;//更新答案
                last_time[now] = j;//更新工序完成的时间
                break;//跳出循环
            }
        }
    }
    printf("%d\n",ans);
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值