算法分析 | 回溯法 | 最佳加工顺序
一.问题分析
1.问题描述:
有n个机器零件,每个零件必须先由机器A处理,再由机器B处理。零件Pi需要机器1,2处理时间为P[i].Atime,P[i].Btime
如何安排零件加工顺序,使第一个零件从机器1上加工开始到最后一个零件在机器2上加工完成,所需的总加工时间最短?
2.A机可以连续不间断开动,B机第i个零件的加工完成时间为:
B_end_time[ i ]=max(B_end_time[ i - 1 ] , A_end_time[ i ]) + P[ i ].Btime
取决于B机处理完前一个零件和A机处理完本零件的最晚时间
3.排列树的概念
回忆递归算法中数组全排列的内容:
a[N]的全排列 = for(0..N-1)将每个元素swap到a[0]一次 + a[N-1]的全排列
在解空间中,每递归深入一层,分支数-1 ,到叶结点时,解的数量=N*(N-1)*(N-2)*(N-3).......=N!
4.约束与限界函数
①约束函数禁止 "根本就不可能出现的情况" ,本题中每一种排列方式都可以出现,所以没有约束函数
②限界函数禁止 "会出现但没必要再扩展的情况". 当目前的B_end_time小于已知的最优解bestn2时,可以试着进行递归
因此,最优解bestn2初始值=INT_MAX
二.代码实现
分为三个区:全局变量区/递归函数/调用递归函数的函数
//BPS is short of "Best Process Sequence" 最佳加工顺序
//找出零件P[n]的一个最佳加工顺序 ,复习数组的全排列问题
//机器A的加工是可以连续不间断的
//机器B,第i个零件的开始加工时间=max(B[ i - 1 ] , A[ i ]) +Btime[ i ]
//将最后的B_end_time赋给bestn2.
struct product
{
int Atime; //记录A机器的加工时间
int Btime; //记录B机器的加工时间
};
vector<product>P = {{5,7},{1,2},{8,2},{5,4},{3,7},{4,4}};
auto M2 = P.size();
int bestn2 = INT_MAX; //记录一次递归结束后的最优值
vector<int> bestx2(M2, 0); //记录一次递归结束后的最优策略
vector<int> cx2(M2, 0); //记录当前策略
int A_end_time; //记录当前A机器的完成时间
int B_end_time; //记录当前B机器(总体)的完成时间
void BPSbacktrack(int t)//t表示子问题的首位,首次递归时t=0
{
if (t >= M2) //说明遍历完0~M1-1个,此时可以记录一个最终最优值,并退出当前递归
{
bestx2 = cx2;
bestn2 = B_end_time;
return;
}
//每一种加工顺序都能得到解,不存在约束条件,只有限界条件
for (int i = t; i < M2; i++) //第一层循环M2次,之后每层--1;
{
A_end_time += P[cx2[i]].Atime;
int temp = B_end_time;
B_end_time = max(A_end_time, B_end_time) + P[cx2[i]].Btime;
if (B_end_time < bestn2) //限界条件
{
swap(cx2[i],cx2[t]);
BPSbacktrack(t + 1);
swap(cx2[i], cx2[t]); //最优策略的回溯
}
//最优值的回溯
A_end_time-= P[cx2[i]].Atime;
B_end_time = temp;
}
}
void BPS()
{
//初始化
for (int i = 0; i < M2; i++)
{
cx2[i] = i;
}
A_end_time = 0; //记录当前A机器的完成时间
B_end_time = 0;
BPSbacktrack(0);
cout << "最优加工顺序: ";
for (int i = 0; i < M2; i++)
{
cout << bestx2[i] << " ";
}cout << endl;
cout << "最短加工时间: " << bestn2;
}

浙公网安备 33010602011771号