回溯算法: 它可以系统的搜索一个问题的所有解或任意解。它的关键点在于处理空间树问题。空间树中对不符合的节点要进行“剪枝”处理 。
回溯思想: 在包含问题的所有解的解空间树中,按照深度优先搜索的策略,从根结点出发深度探索解空间树。当探索到某一结点时,要先判断该结点是否包含问题的解,如果包含,就从该结点出发继续探索下去,如果该结点不包含问题的解,则逐层向其祖先结点回溯。(其实回溯法就是对隐式图的深度优先搜索算法)。 若用回溯法求问题的所有解时,要回溯到根,且根结点的所有可行的子树都要已被搜索遍才结束。 而若使用回溯法求任一个解时,只要搜索到问题的一个解就可以结束。
解空间树: 主要分为子集树与排列树。子集树(局部性):就是对某一集合对象中选取一部分元素构造问题的最优解。排列树(整体性):对含多个任务的作业进行调度。选出相应的排列顺序,从而构造最优解 。
子集树框架:
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 */