回溯法 批处理作业调度问题

问题:

给定n个作业的集合{J1,J2,…,Jn}。每个作业必须先由机器1处理,然后由机器2处理。作业Ji需(1≤i≤n)要机器j(1≤j≤2)的处理时间为 tji。对于一个确定的作业调度,设Fji是作业i在机器j上完成处理的时间。所有作业在机器2上完成处理的时间和称为 该作业调度的完成时间和。
要求对于给定的n个作业,制定最佳作业调度方案,使其完成时间和达到最小。

tji机器1机器2
作业121
作业231
作业323

分析:

由于机器有两个,每个作业必须从机器1处理,再到机器2处理,所以会两个作业同时处理,例如对上面的表格按照(1,2,3)的顺序调度:
(括号内代表作业序号)

 机器1     机器2
  2(1)
  3(2)      1(1)
  2(3)      1(2)
            3(3)

每一个作业的处理时间 = 机器2的处理时间 + 机器2等待处理的时间(包括了之前作业的时间 和 本作业在机器1处理的时间)
所以我们求作业的处理时间,就是求该作业从开始到机器2处理完的时间:

作业1的处理时间:2+1 = 3
作业2的处理时间:2 + 3 + 1 = 6 (作业1的在机器1的处理时间,作业2的在机器1的处理时间 和 作业1在机器2的处理时间的较大时间(因为两者是同时进行的),作业2在机器2的处理时间)
作业3的处理时间:2 + 3 + 2 + 3 = 10

所以(1,2,3)顺序的作业调度得到的作业调度时间为19

再看问题,问题要求得到最优作业调度方案和最小调度时间,也就是要得到使作业调度时间最小的作业处理顺序
因此这是一个 排列树 的问题,用排列树的算法框架:

代码实现

排列树的算法框架有两种实现方式:
(1)用visted方式

#define MAX 200
#include<iostream>
using namespace std;

int number;//作业数量
int x1[10];//作业在机器1运行的时间
int x2[10];//作业在机器2运行的时间
int sum = 0;//作业完成的总时间 
int bestsum = MAX;//作业完成的最优时间 
int order[10];//作业完成的次序,要用于交换
int bestorder[10];//作业完成的最优的顺序
int f1 = 0;//机器1累计的时间 
int f2[10];//作业在机器2处理完累计的时间,即每一个作业的调度时间
int vis[10];//记录作业以否已被选 

void backtrack(int cur) {//cur表示正在赋值的位置,cur+1去到下一层子节点,i递增,在当前层遍历兄弟节点
    if(cur>number) {
        for(int i=1; i<=number; i++) bestorder[i]=order[i];
        bestsum=sum;
    } else {
        for(int i=1; i<=number; i++) { //遍历number,尝试填第一位
            if(!vis[i]) {
                vis[i] = 1;
                f1+=x1[i];
                //本作业的在机器1的处理时间 和 上一个作业在机器2的处理时间的较大时间(因为两者是同时进行的)
                f2[cur]=( f2[cur-1] > f1 ? f2[cur-1] : f1) + x2[i];
                sum+=f2[cur];
                order[cur] = i;
                if(sum<bestsum) {//剪枝,如果当前sum都大于bestsum了,则不再遍历此节点 
                    backtrack(cur+1);
                }
                //每计算一次,为了不影响父节点和兄弟节点,运算完都要复位
                sum-=f2[cur];
                f1-=x1[i];
                vis[i] = 0;
            }
        }
    }
}

int main() {
    cout << "请输入作业的数量: "; 
    cin >> number;
    int i;
    cout << "请输入每个作业在机器1的运行时间:" << endl;
    for(i=1; i<=number; i++){
        cin >> x1[i];
    }
    cout << endl << "请输入每个作业在机器2的运行时间:" << endl;
    for(i=1; i<=number; i++){
        cin >> x2[i];
    }
    //初始化第一个序列,从1开始到number 
    for(i=1; i<=number; i++){
        order[i] = i;
    }
    backtrack(1);
    cout<<"最节省的时间为:"<<bestsum<<endl;  
    cout<<"对应的方案为:";  
    for(i=1;i<=number;i++) cout<<bestorder[i]<<"  ";  
    cout<<endl;  
}

(2)用swap交换方式
除了backtrack,其它代码都没有改变,只贴出backtranck函数的代码:

void backtrack(int cur){
    //到达边界 
    if(cur > number){
        for(int i=1; i<=number; i++){
            bestorder[i] = order[i];
        }
        bestsum = sum;
    }else{
        //cur表示正在赋值的位置,cur+1去到下一层子节点,i递增,在当前层遍历兄弟节点 
        for(int i=cur; i<=number; i++){
            f1 += x1[order[i]];
            f2[cur] = (f2[cur - 1] > f1 ? f2[cur -1] : f1) + x2[order[i]];
            sum += f2[cur];
            /*例如cur为2,i=3时,表示正在为作业队列的第2个位置赋作业3的值,所以此时的队列作业顺序应该是{1,3,2}, 所以要把i和cur的位置换掉;
            同理例如cur为1,i=2时,表示正在为作业队列的第1个位置赋作业2的值,此时的顺序是{2,1,3},好好理解这一点 
            swap函数就是要做这个事情
            */ 
            swap(order[cur], order[i]);
            if(sum < bestsum){//剪枝,如果当前sum都大于bestsum了,则不再遍历此节点 
                backtrack(cur+1);
            }

            swap(order[cur], order[i]);
            sum -= f2[cur];
            f1 -= x1[order[i]];
        }
    }
}

输入上面表格的数据,得出结果:
这里写图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值