一.問題分析
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;
}