算法设计与分析:第五章 回溯法 5.8流水作业车间调度

/*
流水作业车间调度:
n个作业要在两台机器M1和M2组成的流水线上完成加工。每个作业加工的顺序都是现在M1上加工,然后在
M2上加工。M1和M2加工作业i所需的时间分别为ai和bi。流水作业调度问题要求确定这n个作业的最优
加工顺序,使得从第一个作业在机器M1上开始加工,到最后一个作业在机器M2上加工完成所需的时间
最少。作业在机器M1和M2的加工顺序相同。

分析:
解空间:是排列树,用数组x[](初值为1,2,3,...,n)模拟不同排列
如何计算完成时间?
对机器M1进行加工,加工时间f1是固定的。f1[i] = f1[i-1] + M1[x[i]]
对机器M2加工分为两种情况
1)有空闲
##a1........##a2...........##
			##b1....       ##b2.........##
空闲时,f2[i] = f1[i] + M2[x[i]]

2)积压时
##a1........##a2...........##
			##b1..................##b2.........##
空闲时,f2[i] = f2[i-1] + M2[x[i]]

如何获取最优解?
最优调度应使及其M1没有空闲时间,机器M2空闲时间最少。
在搜索排列树的同时,不断更新最优解,最后找到问题的最优解。
搜索过程中,当某一排列的前几步的加工时间已经大于当前的最小值,就无需搜索计算。

数据结构设计:
二维数组job[100][2]存储作业在M1,M2的加工时间。
由于f1在计算中,只需当前值,用变量存储即可;f2在计算时,还依赖前一个作业的数据,
所以用数组存储。
变量f存储当前加工需要的全部时间。

输入:
3(作业数)
2 1
3 1 
2 3
输出:
8

算法步骤:
1 如果搜索到叶节点(必然是最优的),得到调度方案,更新
2 对当前作业直到最后作业做迭代
3 计算机器1工作时间 , 判断机器2处于积压或者空闲 并更新f2
4 更新直到当前结点的累加值
5 若累加值小于最优解,则进入该结点的叶子结点,交换两个作业,回溯下一个结点,再交换回来
6 将f1设置为回溯前的值,将累加值还原为回溯前的值
7 这是排列树问题,初始化应该给与一个排列
		//这是回溯中的排列树问题:需要赋予一个初始排列。排列树:选取的n个元素满足某种排列性质,时间复杂度为O(n!)
		for(int k = 1 ; k <= n ; k++)
		{
			//设置一个初始调度为调度作业1,2,...,n的顺序
			x[k] = k;
		}
8 初始最优值应该置为无穷大
bestf = 1000000000;//易错,最优值应该初始化为无穷大
*/

#include 

using namespace std;

int n;//作业数
int bestf;//存放最优调度时间
int f1;//机器1的调用时间
int f2[100];//机器2的调用时间
int f;//直至当前结点的机器调用累加时间
int job[100][3];//作业的运行时间
int x[100];//易错,当前调度
int bestx[100];//易错,存放当前作业最佳调度

void swap(int* p1 , int* p2)
{
	int iTemp = *p1 ; 
	*p1 = *p2;
	*p2 = iTemp;
}

void backTrace(int i)
{
	if(i == n + 1)//如果到达叶子结点,则说明产生了调度方案,更新
	{
		for(int j = 1 ; j <= n ; j++)
		{
			bestx[j] = x[j];
		}
		bestf = f;
	}
	else
	{
		//回溯的标准格式
		for(int j = i ; j <= n  ; j++)
		{
			f1 = f1 + job[ x[j] ][1];
			//如果机器2存在积压
			if(f2[i-1] > f1)
			{
				f2[i] = f2[i-1] + job[ x[j] ][2];
			}
			else
			{
				f2[i] = f1 + job[ x[j] ][2];
			}
			f = f + f2[i];
			//判断累加和是否小于最优调度值,是就进行继续尝试下一个结点
			if(f < bestf)
			{
				swap(&x[i] , &x[j]);
				backTrace(i+1);
				swap(&x[i] , &x[j]);
			}
			//清理现场
			f1 = f1 - job[ x[j] ][1];
			f = f - f2[i];
		}
	}
}

void process()
{
	while(cin >> n)
	{
		//输入
		memset(job , 0 , sizeof(job));
		for(int i = 1 ; i <= n ; i++)
		{
			for(int j = 1 ; j <= 2 ; j++)
			{
				cin >> job[i][j];
			}
		}
		//计算
		f = 0;
		f1 = 0;
		bestf = 1000000000;//易错,最优值应该初始化为无穷大
		memset(f2 , 0 , sizeof(f2));
		//易错,这是回溯中的排列树问题:需要赋予一个初始排列。排列树:选取的n个元素满足某种排列性质,时间复杂度为O(n!)
		for(int k = 1 ; k <= n ; k++)
		{
			//设置一个初始调度为调度作业1,2,...,n的顺序
			x[k] = k;
		}
		backTrace(1);
		cout << f2[n] << endl;
	}
}

int main(int argc , char* argv[])
{
	process();
	getchar();
	return 0;
}

你可能感兴趣的:(算法设计与分析)