回溯算法-排列树-批处理作业调度

回溯算法: 它可以系统的搜索一个问题的所有解或任意解。它的关键点在于处理空间树问题。空间树中对不符合的节点要进行“剪枝”处理 。

回溯思想: 在包含问题的所有解的解空间树中,按照深度优先搜索的策略,从根结点出发深度探索解空间树。当探索到某一结点时,要先判断该结点是否包含问题的解,如果包含,就从该结点出发继续探索下去,如果该结点不包含问题的解,则逐层向其祖先结点回溯。(其实回溯法就是对隐式图的深度优先搜索算法)。 若用回溯法求问题的所有解时,要回溯到根,且根结点的所有可行的子树都要已被搜索遍才结束。 而若使用回溯法求任一个解时,只要搜索到问题的一个解就可以结束。

解空间树: 主要分为子集树与排列树。子集树(局部性):就是对某一集合对象中选取一部分元素构造问题的最优解。排列树(整体性):对含多个任务的作业进行调度。选出相应的排列顺序,从而构造最优解 。

 子集树框架:

void Backpack(int t){        /*x集合元素个数*/
     if(t>n){               
            output(x);    /*到达叶子节点,记录状态或输出最终结果*/
     }else{
         for(int i = 0;i<=1;i++){
             x[t] = i;      /*二叉树排列*/  
         } 
         /*constraint约束函数Bound上界函数*/
         if(constraint(t)&&Bound(t)){  
            Backpack(t+1);
         }
     }
 }
排列树框架:
 void Backpack(int t){        /*x作业个数*/
     if(t>n){               
            output(x);    /*到达叶子节点,记录状态或输出最终结果*/
     }else{
         for(int i = t;i<=n;i++){
           if(constraint(t)&&Bound(t)){  
            swap(x[t],x[i]);
            Backpack(t+1);
            swap(x[i],x[t]);
         } 
     }
 }

作业调度:

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

批处理作业调度问题要求对于给定的n个作业,制定最佳作业调度方案,使其完成时间和达到最小

  例:设n=3,考虑以下实例:


     这3个作业的6种可能的调度方案是1,2,3;1,3,2;2,1,3;2,3,1;3,1,2;3,2,1;它们所相应的完成时间和分别是19,18,20,21,19,19。易见,最佳调度方案是1,3,2,其完成时间和为18。

      结果得来:选择顺序(1->3->2):  作业1:在机器1上完成时间2,机器2上完成时间为2+1 = 3。对作业3:在机器1上完成时间2+2 =4。作业3在机器1上完成时间(4)大于作业1在机器2上完成时间(3),所以作业3在机器2上完成时间为4+3 = 7。同理作业2:机器1时间为7,机器2时间为8。tol = 7+3+8 = 18。(ps: 作业t在机器2完成时间由上一作业在机器2上完成时间和本次作业在机器1上完成的时间的较大者决定)

    批处理作业调度问题要从n个作业的所有排列中找出具有最小完成时间和的作业调度,所以如图,批处理作业调度问题的解空间是一颗排列树     

代码:
/**
   @回溯法-批处理作业调度
*/
#include<iostream>
#include<algorithm>
using namespace std;
#define MAX 100
int f1 = 0;                  //作业1完成时间
int f2[MAX] = {0};          //作业2完成时间
int f = 0;                 //总共完成时间
int a[MAX][MAX];          //作业在机器上所需时间
int best[MAX];           //最优作业的编号顺序
int x[MAX];             //记录作业编号
int bestf = 63355;     //最优时间值
int backpack(int t,int n){
    if(t>n){
        for(int i = 1;i<=n;i++){
            best[i] = x[i];
        }
            bestf = f;    //确定当前最优值
     }
     for(int i = t;i<=n;i++){
         f1+=a[1][x[i]];
    /*作业t在机器2完成时间由上一作业在机器2上完成时间和本次作业在机器1上完成的时间的较大者决定*/
         f2[t] = ((f2[t-1]>f1)?f2[t-1]:f1)+a[2][x[i]];
         f+=f2[t];
         if(f<bestf){  //约束为调换顺序后当前作业时间花费要小于先前记录时间
            swap(x[i],x[t]);
            backpack(t+1,n);     //往下向子节点继续探索
            swap(x[t],x[i]);
         }
           /*回到上一层在当前节点处再进行其它方向探索*/
           f1-=a[1][x[i]];
           f-=f2[t];
     }
}
int main(){
    int n;                    //作业个数
    cin>>n;
    /*矩阵行列初始化*/
//    for(int i = 0;i<n;i++){
//        a[i][0] = 0;
//    }
//    for(int i = 0;i<=n;i++){
//        a[0][i] = 0;
//    }
    for(int i = 1;i<=n;i++){        //作业编号处理
           x[i] = i;
    }
    for(int i = 1;i<n;i++){
        for(int j = 1;j<=n;j++){
            cin>>a[i][j];
        }
    }
    backpack(1,n);
    cout<<"最优处理时间为:"<<bestf<<endl;
    cout<<"最优排序为:";
    for(int i = 1;i<=n;i++){
          cout<<best[i];
          if(i!=n){
            cout<<"-";
          }
    }
   return 0;
}
/*
3
2 3 2  各作业在机器1上使用时间
1 1 3  各作业在机器2上使用时间
处理后为顺序:(1->3->2)
2 4 7
3 7 8
tol:3+7+8 = 19
*/



猜你喜欢

转载自blog.csdn.net/lfb637/article/details/80579443