回溯法 作业
- 格式:ppt
- 大小:228.50 KB
- 文档页数:7
算法设计与分析——批处理作业调度(回溯法)之前讲过⼀个相似的问题流⽔作业调度问题,那⼀道题最开始⽤动态规划,推到最后得到了⼀个Johnson法则,变成了⼀个排序问题,有兴趣的可以看⼀下本篇博客主要参考⾃⼀、问题描述给定n个作业的集合{J1,J2,…,Jn}。
每个作业必须先由机器1处理,然后由机器2处理。
作业Ji需要机器j的处理时间为t ji。
对于⼀个确定的作业调度,设Fji是作业i在机器j上完成处理的时间。
所有作业在机器2上完成处理的时间和称为该作业调度的完成时间和。
批处理作业调度问题要求对于给定的n个作业,制定最佳作业调度⽅案,使其完成时间和达到最⼩。
例:设n=3,考虑以下实例:看到这⾥可能会对这些完成时间和是怎么计算出来的会有疑问,这⾥我拿123和312的⽅案来说明⼀下。
对于调度⽅案(1,2,3)作业1在机器1上完成的时间是2,在机器2上完成的时间是3作业2在机器1上完成的时间是5,在机器2上完成的时间是6作业3在机器1上完成的时间是7,在机器2上完成的时间是10所以,作业调度的完成时间和= 3 + 6 + 10这⾥我们可以思考⼀下作业i在机器2上完成的时间应该怎么去求?作业i在机器1上完成的时间是连续的,所以是直接累加就可以。
但对于机器2就会产⽣两种情况,这两种情况其实就是上图的两种情况,对于(1,2,3)的调度⽅案,在求作业2在机器2上完成的时间时,由于作业2在机器1上还没有完成,这就需要先等待机器1处理完;⽽对于(3,1,2)的调度⽅案,在求作业2在机器2上完成的时间时,作业2在机器1早已完成,⽆需等待,直接在作业1被机器1处理之后就能接着被处理。
综上,我们可以得到如下表达式if(F2[i-1] > F1[i])F2[i] = F2[i-1] + t[2][i]elseF2[i] = F1[i] + t[2][i]⼆、算法设计类Flowshop的数据成员记录解空间的结点信息,M输⼊作业时间,bestf记录当前最⼩完成时间和,数组bestx记录相应的当前最佳作业调度。
1.8 售货员的难题(salesman.pas)【问题描述】某乡有n个村庄(1<n<15),有一个售货员,他要到各个村庄去售货,各村庄之间的路程s(0<s<1000)是已知的,且A村到B村与B村到A村的路大多不同。
为了提高效率,他从商店出发到每个村庄一次,然后返回商店所在的村,假设商店所在的村庄为1,他不知道选择什么样的路线才能使所走的路程最短。
请你帮他选择一条最短的路。
【输入】村庄数n和各村之间的路程(均是整数)。
【输出】最短的路程。
【样例】salesman,in3 {村庄数}0 2 1 {村庄1到各村的路程}1 02 {村庄2到各村的路程}2 1 0 {村庄3到各村的路程}salesman.out31.9 驾车旅行(tour.pas)【问题描述】如今许多普通百姓家有了私家车,一些人喜爱自己驾车从一个城市到另一个城市旅游。
自己驾车旅游时总会碰到加油和吃饭的问题,在出发之前,驾车人总要想方设法得到从一个城市到另一个城市路线上的加油站的列表,列表中包括了所有加油站的位置及其每升的油价(如3.25元/L)。
驾车者一般都有以下的习惯:⑴除非汽车无法用油箱里的汽油达到下一个加油站或目的地,在油箱里还有不少于最大容量一半的汽油时,驾驶员从不在加油站停下来;(2)在每个停下的加油站总是将油箱加满;(3)在加油站加油的同时,买快餐等吃的东西花去20元。
(4)从起始城市出发时油箱总是满的。
(5)加油站付钱总是精确到0.1元(四舍五入)。
(6)驾车者都知道自己的汽车每升汽油能够行驶的里程数。
现在要你帮忙做的就是编写一个程序,计算出驾车从一个城市到另一个城市的旅游在加油和吃饭方面最少的费用。
【输入】第一行是一个实数,是从出发地到目的地的距离(单位:km)。
第二行是三个实数和一个整数,其中第一个实数是汽车油箱的最大容量(单位:L);第二个实数是汽车每升油能行驶的公里数;第三个实数是汽车在出发地加满油箱时的费用(单位:元),一个整数是1到50间的数,表示从出发地到目的地线路上加油站的数目。
火柴游戏• 有红,绿,蓝三种颜色的火柴,所有火柴长度一样。
用它们可以组成一些数字, 如下图所示:•• 为了让组成的火柴好看一些,我们规定:有公共点的火柴必须不同色。
例如,用红火柴4根和蓝火柴3根可以组成数12,21,7等。
而且有些方案虽然数字和它们的排列顺序都相同,但是有颜色之分。
问:一共可以组成多少个数(可以包含多个数字,但至少包含一个数字)?同一个数用不同颜色表示算作不同的数,火柴可以有剩余。
• 输入• 三个整数n1, n2, n3 (0 ≤ n1,n2,n3 ≤ 15),即三种颜色的火柴的数目。
• 输出• 仅一个数,即可以组成的数的数目。
结果保证不超过1016。
骑士游历问题设有一个n*m 的棋盘(2≤n ≤50,2≤m ≤50),如下图。
在棋盘上任一点有一个中国象棋马,马走的规则为:1.马走日字2.马只能向右走。
即左图所示:当n ,m 给出之后,同时给出马起始的位置和终点的位置,试找出从起点到终点的所有路径的数目。
例如:(n=10,m=10),(1,5)(起点),(3,5)(终点)。
应输出2(即由(1,5)到(3,5)共有2条路径,如下图):输入:n ,m ,x1,y1,x2,y2(分别表示n ,m ,起点坐标,终点坐标) 输出:路径数目(若不存在从起点到终点的路径,输出0)过河卒如图,A点有一个过河卒,需要走到目标B点。
卒行走的规则:可以向下、或者向右。
••同时在棋盘上的任一点有一个对方的马(如上图的C点),该马所在的点和所有跳跃一步可达的点称为对方马的控制点。
例如上图C点上的马可以控制8个点(图中的P1,P2….P8)。
卒不能通过对方马的控制点。
•棋盘用坐标表示,A点(0,0)、B点(n,m)(n,m为不超过20的整数,并由键盘输入),同样马的位置坐标是需要给出的(约定:C≠A,同时C≠B)。
现在要求你计算出卒从A点能够到达B点的路径的条数。
•输入:•键盘输入B点的坐标(n,m)以及对方马的坐标(X,Y)输出:•屏幕输出一个整数(路径的条数)。
回溯法习题汇总1.1 马拦过河卒源程序名knight.???(pas, c, cpp)可执行文件名knight.exe输入文件名knight.in输出文件名knight.out【问题描述】棋盘上A点有一个过河卒,需要走到目标B点。
卒行走的规则:可以向下、或者向右。
同时在棋盘上C点有一个对方的马,该马所在的点和所有跳跃一步可达的点称为对方马的控制点。
因此称之为“马拦过河卒”。
棋盘用坐标表示,A点(0, 0)、B点(n, m)(n, m为不超过15的整数),同样马的位置坐标是需要给出的。
现在要求你计算出卒从A点能够到达B点的路径的条数,假设马的位置是固定不动的,并不是卒走一步马走一步。
【输入】一行四个数据,分别表示B点坐标和马的坐标。
【输出】一个数据,表示所有的路径条数。
【样例】knight.in knight.out6 6 3 3 6【算法分析】从起点开始往下走(只有两个方向可以走),如果某个方向可以走再继续下一步,直到终点,此时计数。
最后输出所有的路径数。
这种方法可以找出所有可能走法,如果要输出这些走法的话这种方法最合适了,但是本题只要求输出总的路径的条数,当棋盘比较大时,本程序执行会超时,此时最好能找出相应的递推公式更合适,详见后面的递推章节。
1.2 出栈序列统计源程序名stack1.???(pas, c, cpp)可执行文件名stack1.exe输入文件名stack1.in输出文件名stack1.out【问题描述】栈是常用的一种数据结构,有n令元素在栈顶端一侧等待进栈,栈顶端另一侧是出栈序列。
你已经知道栈的操作有两·种:push和pop,前者是将一个元素进栈,后者是将栈顶元素弹出。
现在要使用这两种操作,由一个操作序列可以得到一系列的输出序列。
请你编程求出对于给定的n,计算并输出由操作数序列1,2,…,n,经过一系列操作可能得到的输出序列总数。
【输入】一个整数n(1<=n<=15)【输出】一个整数,即可能输出序列的总数目。
1、批作业调度问题(1)问题描述给定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。
(2)算法设计批处理作业调度问题要从n个作业的所有排列中找出具有最小完成时间和的作业调度,所以如图,批处理作业调度问题的解空间是一颗排列树。
按照回溯法搜索排列树的算法框架,设开始时x=[1,2,……n]是所给的n个作业,则相应的排列树由x[1:n]的所有排列构成。
算法具体代码如下:[cpp]view plain copy1.#include "stdafx.h"2.#include <iostream>ing namespace std;4.5.class Flowshop6.{7.friend int Flow(int **M,int n,int bestx[]);8.private:9.void Backtrack(int i);10.11.int **M, // 各作业所需的处理时间12. *x, // 当前作业调度13. *bestx, // 当前最优作业调度14.15. *f2, // 机器2完成处理时间16. f1, // 机器1完成处理时间17. f, // 完成时间和18.19. bestf, // 当前最优值20. n; // 作业数21. };22.23.int Flow(int **M,int n,int bestx[]);24.25.template <class Type>26.inline void Swap(Type &a, Type &b);27.28.int main()29.{30.int n=3,bf;31.int M1[4][3]={{0,0,0},{0,2,1},{0,3,1},{0,2,3}};32.33.int **M=new int*[n+1];34.35.for(int i=0;i<=n;i++)36. {37. M[i]=new int[3];38. }39. cout<<"M(i,j)值如下:"<<endl;40.41.for(int i=0;i<=n;i++)42. {43.for(int j=0;j<3;j++)44. {45. M[i][j]=M1[i][j];46. }47. }48.49.int bestx[4];50.for(int i=1;i<=n;i++)51. {52. cout<<"(";53.for(int j=1;j<3;j++)54. cout<<M[i][j]<<" ";55. cout<<")";56. }57.58. bf=Flow(M,n,bestx);59.60.for(int i=0;i<=n;i++)61. {62.delete []M[i];63. }64.delete []M;65.66. M=0;67.68. cout<<endl<<"最优值是:"<<bf<<endl;69. cout<<"最优调度是:";70.71.for(int i=1;i<=n;i++)72. {73. cout<<bestx[i]<<" ";74. }75. cout<<endl;76.return 0;77.}78.79.void Flowshop::Backtrack(int i)80.{81.if (i>n)82. {83.for (int j=1; j<=n; j++)84. {85. bestx[j] = x[j];86. }87. bestf = f;88. }89.else90. {91.for (int j = i; j <= n; j++)92. {93. f1+=M[x[j]][1];94.//机器2执行的是机器1已完成的作业,所以是i-195. f2[i]=((f2[i-1]>f1)?f2[i-1]:f1)+M[x[j]][2];96.97. f+=f2[i];98.if (f < bestf)//剪枝99. {100. Swap(x[i], x[j]); 101. Backtrack(i+1); 102. Swap(x[i], x[j]); 103. }104. f1-=M[x[j]][1];105. f-=f2[i];106. }107. }108.}109.110.int Flow(int **M,int n,int bestx[]) 111.{112.int ub=30000;113.114. Flowshop X;115. X.n=n;116. X.x=new int[n+1];117. X.f2=new int[n+1];118.119. X.M=M;120. X.bestx=bestx;121. X.bestf=ub;122.123. X.f1=0;124. X.f=0;125.126.for(int i=0;i<=n;i++)127. {128. X.f2[i]=0,X.x[i]=i;129. }130. X.Backtrack(1);131.delete []X.x;132.delete []X.f2;133.return X.bestf;134.}135.136.template <class Type>137.inline void Swap(Type &a, Type &b) 138.{139. Type temp=a;140. a=b;141. b=temp;142.}由于算法Backtrack在每一个节点处耗费O(1)计算时间,故在最坏情况下,整个算法计算时间复杂性为O(n!)。
回溯算法---解决批处理作业调度问题1、问题的描述n个作业{1, 2, …, n}要在两台机器上处理,每个作业必须先由机器1处理,然后再由机器2处理,机器1处理作业i所需时间为ai,机器2处理作业i 所需时间为bi(1≤i≤n),批处理作业调度问题要求确定这n个作业的最优处理顺序,使得从第1个作业在机器1上处理开始,到最后一个作业在机器2上处理结束所需时间最少。
利用回溯法解决批作业最优调度问题。
算法思想:对于有n个不同的任务,搜索出其最佳排列,属于一棵排列树搜索问题。
采用回溯法,搜索到第t层时,当已经是叶子节点时,说明该路径就是当前情况下的最优解。
当t不是叶子节点时,依次按照一定的顺序执行当前剩下的任务,将剩下的任务全部遍历一遍。
计算执行当前任务后,各个时间点的变化。
如果该层某个节点的任务执行之后,依然符合剪枝函数,则将当前的策略顺序做出调整,将刚刚执行的那个节点的任务序号放置到当前,然后继续向下进行搜索。
剪枝函数:当当前节点的总时间已经大于已找到的最优时间,则该节点后面的节点都不用进行搜索。
直接回溯。
2、代码及注释#include <iostream>using namespace std;#define MAX 1111111111//宏定义设置一个在适当范围内足够大的数字,方便以后进行比较//定义全局变量,包含如下int* x1;//作业Ji在机器1上的工作时间;int* x2;//作业Ji在机器2上的工作时间;int number=0;//作业的数目;int* xOrder;//作业顺序;int* bestOrder;//最优的作业顺序;int bestValue=MAX;//最优的时间;int xValue=0;//当前完成用的时间;int f1=0;//机器1完成的处理时间;int* f2;//第i阶段机器2完成的时间;//定义一个递归调用函数,找出最优void BackTrace(int k){if (k>number)//算法搜索至叶子结点,{for (int i=1;i<=number;i++){bestOrder[i]=xOrder[i];//得到一个新的作业调度方案。