相比回溯法搜尋,分支限界法是一種啟發式搜尋策略。
分支與限界法不再單純的像回溯法那樣盲目的往前搜尋,也不是遇到死胡同才往回走,而是依據節點表中不斷更新的資訊(此處的資訊可根據具體情況具體選擇),不斷的調整自己的搜尋方向,有選擇,有目的地往前搜尋;回溯也不是單純地沿著父親節點,一層一層地向上回溯,而是依據節點表中的資訊進行回溯。
實現要求:
n個操作員以n種不同時間完成n項不同的作業,要求分配每位操作員完成一項作業,使完成n項作業的時間總和最小。
本次實驗以4個操作員4份工作為例,資料如下所示,第2行第2列的12表示第2個操作員完成第二項工作需要時間12。38
令tik表示在深度k下,把作業分配給操作員i時的時間下界,那麼如圖所示,k=0時有:(計算下界的方式為當前值+後續值的最小值且不考慮每個員工只做一項工作的約束,所以只是個下界,並且確定值)
t00 = 3+7+6+3=19
t10 = 9+7+4+3=23
t20 = 8+7+4+5=24
t30 = 12+7+4+3=26
下界值小於bound(9999),根據下界值從小打到插入優先佇列。隊首元素t00。【bound值僅在某一結點達到葉節點時更新】
從優先佇列彈出隊首元素,並且生成相應的孩子節點,如圖為6,7,8,對應於把1號作業分配給剩餘操作員的情況,計算下界。
t11=3+12+6+3=24
t21=3+7+6+5=21
t31=3+7+9+3=22
同樣按照下界大小插入到優先佇列,例如t21即7號節點下界為21,比當前節點和先前節點都小,所以它才是隊首元素。
迴圈這個過程,直到某個節點到達隊首並且滿足為葉節點時,返回,此節點就是所要求的最優值。
(1) 建立根節點x,令根節點的深度x.k=0,未分配作業操作員編號集合x.s=,已分配作業操作員編號的x.m=fai,把當前問題最優下界bound置999999.
(2) 令i=0
(3) 若i位於x.s,建立兒子節點yi,把節點x的資料複製到節點yi,否則轉步驟 (7)
(4) 令yi.m=yi.m並,yi.s=yi.s - , yi.xi=yi=k, yi.k = yi.k+1(多乙個值,意味著深度加1,每個操作員都分配就到了葉節點深度),如上所示計算深度yi.t。
(5) 如果yi.t(6) 把節點yi按t從小打到插入優先佇列,如果yi時葉子節點,說明它是問題的乙個可行解,用yi.t更新當前的可行解最優時間下界bound。
(7) i = i+1,若i(8) 取下佇列首元素作為子樹的根節點x,若x.k=n,則該節點是葉子節點,表明它是問題的最優解,演算法結束,向量x.x便是最優方案;否則轉步驟(2)
#includeusing namespace std;
#define max_num 99999
const int n = 4;
float c[n][n];//n個操作員分別完成n項作業所需時間
float bound = max_num;//當前已搜尋可行解的最優時間
struct ass_node ;
typedef struct ass_node* ass_node;
//把xnode所指向的節點按所需時間下界插入優先佇列qbase中,下界越小,優先性越高
void q_insert(ass_node qbase, ass_node xnode)
temp2 = temp;
temp = temp->next;
} xnode->next = temp2->next;
temp2->next = xnode;
}//取下並返回優先佇列qbase的首元素
ass_node q_delete(ass_node qbase)
//分支限界法實現
float job_assigned(float (*c)[n], int n, int* job)
ynode->b += min;//本節點所需時間下界
}if (ynode->b < bound)
else delete ynode;//大於可行解最優下界
}} delete xnode;//釋放節點xnode的緩衝區
xnode = q_delete(qbase);//取下佇列首元素xnode
} min = xnode->b;
for (i = 0;i < n;i++)//儲存最優方案
job[i] = xnode->x[i];
while (qbase->next)
return min;
}int main()
結構體程式設計需要注意:
結構體指標* p和* q,如果p=q賦值,q變p也變,但是*p = *q相當於深拷貝,並不會出現類似情況,剛開始在未提前設定煉表頭節點時出了一大堆bug都是因為賦值產生,需要注意了!!!!
最好的建議是提前生成乙個空的頭指標,這樣在插入和刪除操作中都會簡單很多,不然插入刪除考慮生成和刪除頭節點等不必要的麻煩,很頭痛的。
附上當時未分配頭節點寫的課後6-4零件的指定重量下最小**的**,和作業分配**稍有不同,因為兩個零件可以來自同一商家。
#include#includeusing namespace std;
#define max_num 99999
int bound = max_num;
ifstream fi("input.txt");
ofstream fo("output.txt");
struct ass_node ;
typedef struct ass_node* ass_node;
//把xnode所指向的節點按所需時間下界插入優先佇列qbase中,下界越小,優先性越高
void q_insert(ass_node qbase, ass_node xnode)
ass_node temp = qbase;
ass_node temp2 = qbase;
while (temp != null)
temp2 = temp;
temp = temp->next;
} xnode->next = temp2->next;
temp2->next = xnode;
}//取下並返回優先佇列qbase的首元素
ass_node q_delete(ass_node qbase,int n)
//優先佇列分支限界法
int job_assigned(int** c, int** w, int n,int m, int d, int* job)
ynode->b += min;//本節點所需**下界
}//更新bound
if (ynode->b < bound && ynode->w <= d)
else
q_insert(qbase, ynode);//把節點插入優先佇列
if (ynode->k == n)//得到乙個可行解
bound = ynode->b;//更新可行解的最優下界
}else delete ynode;//不滿足最優解
} delete xnode;//釋放節點xnode的緩衝區
//出隊
xnode = q_delete(qbase, n);//取下佇列首元素xnode
} //輸出
min = xnode->b;
for (i = 0;i < n;i++)//儲存最優方案
job[i] = xnode->x[i];
while (qbase)
else
} return min;
}int main()
int** w = new int *[n];
for (i = 0;i < n;i++)
int* fenpei = new int[n];
for (int i = 0;i < n;i++)
fenpei[i] = -1;//-1表示第i個部件未分配商家
int result = job_assigned(c, w, n,m, d, fenpei);
for (int i = 0;i < n;i++)
/*fo << fenpei[i]<
cout << fenpei[i] << " ";
cout << endl;
fo << endl;
fo << result;
cout << result << endl;
system("pause");
return 1;
}
分支限界法
分支限界法 類似於回溯法,也是一種在問題的解空間樹t上搜尋問題解的演算法。但在一般情況下,分支限界法與回溯法的求解目標不同。回溯法 的求解目標是找出t中滿足約束條件的 所有解,而分支限界法的求解目標則是找出滿足約束條件的乙個解,或是在滿足約束條件的解中找出使某一目標函式值達到極大或極小的解,即在某種...
分支限界法
分支限界法 類似於回溯法,也是一種在問題的解空間樹t上搜尋問題解的演算法。但在一般情況下,分支限界法與回溯法的求解目標不同。回溯法 的求解目標是找出t中滿足約束條件的 所有解,而分支限界法的求解目標則是找出滿足約束條件的乙個解,或是在滿足約束條件的解中找出使某一目標函式值達到極大或極小的解,即在某種...
分支限界法
分支限界法 類似於回溯法,也是一種在問題的解空間樹t上搜尋問題解的演算法。但在一般情況下,分支限界法與回溯法的求解目標不同。回溯法 的求解目標是找出t中滿足約束條件的 所有解,而分支限界法的求解目標則是找出滿足約束條件的乙個解,或是在滿足約束條件的解中找出使某一目標函式值達到極大或極小的解,即在某種...