回溯法(2)
二、批处理作业调度
问题表述:给定n个作业的集合{J1,J2,…,Jn}。每个作业必须先由机器1处理,然后由机器2处理。作业Ji需要机器j的处理时间为tji。对于一个确定的作业调度,设Fji是作业i在机器j上完成处理的时间。所有作业在机器2上完成处理的时间和称为该作业调度的完成时间和。
批处理作业调度问题要求对于给定的n个作业,制定最佳作业调度方案,使其完成时间和达到最小。
显然,1,3,2是最佳调度方案。
解空间:排列树(将作业顺序进行全排列,分别算出各种情况的完成时间和,取最佳调度方案)
实现:
/* 主题:批处理作业调度算法 * 作者:chinazhangjie * 邮箱:chinajiezhang@gmail.com * 开发语言:C++ * 开发环境:Mircosoft Virsual Studio 2008 * 时间: 2010.10.24 */ #include <iostream> #include <vector> using namespace std; class flowshop { public: flowshop(vector<vector<int> >& rhs) { task_count = rhs.size() ; each_t = rhs ; best_t.resize (task_count) ; machine2_t.resize (task_count,0) ; machine1_t = 0 ; total_t = 0 ; best_total_t = 0 ; current_t.resize (task_count,0) ; for (int i = 0 ;i < task_count; ++ i) { current_t[i] = i; // 为了实现全排列 } } void backtrack () { __backtrack (0); // 显示最佳调度方案和最优完成时间和 cout << "the best flowshop scheme is : "; copy (best_t.begin(),best_t.end(),ostream_iterator<int> (cout, " ")); cout << endl; cout << "the best total time is : " << best_total_t << endl; } private: void __backtrack (int i) { if (i >= task_count) { if (total_t < best_total_t || best_total_t == 0) { // 存储当前最优调度方式 copy (current_t.begin(),current_t.end(),best_t.begin()) ; best_total_t = total_t; } return ; } for (int j = i; j < task_count; ++ j) { // 机器1上结束的时间 machine1_t += each_t[current_t[j]][0] ; if (i == 0) { machine2_t[i] = machine1_t + each_t[current_t[j]][1] ; } else { // 机器2上结束的时间 machine2_t[i] = ((machine2_t[i - 1] > machine1_t) ? machine2_t[i - 1] : machine1_t) + each_t[current_t[j]][1] ; } total_t += machine2_t[i]; // 剪枝 if (total_t < best_total_t || best_total_t == 0) { // 全排列 swap (current_t[i],current_t[j]) ; __backtrack (i + 1) ; swap (current_t[i],current_t[j]) ; } machine1_t -= each_t[current_t[j]][0] ; total_t -= machine2_t[i] ; } } public : int task_count ; // 作业数 vector<vector<int> > each_t ; // 各作业所需的处理时间 vector<int> current_t ; // 当前作业调度 vector<int> best_t ; // 当前最优时间调度 vector<int> machine2_t ; // 机器2完成处理的时间 int machine1_t ; // 机器1完成处理的时间 int total_t ; // 完成时间和 int best_total_t ; // 当前最优完成时间和 }; int main() { // const int task_count = 4; const int task_count = 3 ; vector<vector<int> > each_t(task_count) ; for (int i = 0;i < task_count; ++ i) { each_t[i].resize (2) ; } each_t[0][0] = 2 ; each_t[0][1] = 1 ; each_t[1][0] = 3 ; each_t[1][1] = 1 ; each_t[2][0] = 2 ; each_t[2][1] = 3 ; // each_t[3][0] = 1 ; // each_t[3][1] = 1 ; flowshop fs(each_t) ; fs.backtrack () ; }
三、n后问题
问题表述:在n×n格的棋盘上放置彼此不受攻击的n个皇后。按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。n后问题等价于在n×n格的棋盘上放置n个皇后,任何2个皇后不放在同一行或同一列或同一斜线上。求不同的解的个数。
解向量:(x1, x2, … , xn)
显约束:xi = 1,2, … ,n
隐约束:
1)不同列:xi != xj
2)不处于同一正、反对角线:|i-j| != |x(i)-x(j)|
解空间:满N叉树
实现:
/* 主题:n后问题 * 作者:chinazhangjie * 邮箱:chinajiezhang@gmail.com * 开发语言:C++ * 开发环境:Mircosoft Virsual Studio 2008 * 时间: 2010.10.24 */ #include <iostream> #include <vector> using namespace std; class queen { // 皇后在棋盘上的位置 struct q_place { int x; int y; q_place () : x(0),y(0) {} }; public: queen(int qc) : q_count (qc), sum_solution (0) { curr_solution.resize (q_count); } void backtrack () { __backtrack (0); } private: void __backtrack (int t) { if (t >= q_count) { // 找到一个解决方案 ++ sum_solution ; for (size_t i = 0;i < curr_solution.size(); ++ i) { cout << "x = " << curr_solution[i].x << " y = " << curr_solution[i].y << endl; } cout << "sum_solution = " << sum_solution << endl; } else { for (int i = 0;i < q_count; ++ i) { curr_solution[t].x = i; curr_solution[t].y = t; if (__place(t)) { __backtrack (t + 1); } } } } // 判断第k个皇后的位置是否与前面的皇后相冲突 bool __place (int k) { for (int i = 0; i < k; ++ i) { if ((abs(curr_solution[i].x - curr_solution[k].x) == abs(curr_solution[i].y - curr_solution[k].y)) || curr_solution[i].x == curr_solution[k].x) { return false; } } return true; } private: vector<q_place> curr_solution; // 当前解决方案 const int q_count; // 皇后个数 int sum_solution; // 当前找到的解决方案的个数 }; int main() { queen q(5); q.backtrack (); return 0; }
参考资料 《算法设计与分析》王晓东编著
授课教师 张阳教授