一、實驗內容
運用分支限界法解決0-1背包問題(或者旅行售貨員問題、或者裝載問題、或者批處理作業調度)
使用優先隊列式分支限界法來求解旅行售貨員問題
二、所用算法基本思想及復雜度分析
1.算法基本思想
分支限界法常以廣度優先或以最小耗費有限的方式搜索問題的解空間樹。問題的解空間樹是表示問題解空間的一棵有序樹,常見的有子集樹和排列樹。在搜索問題的解空間樹時,分支限界法和回溯法的主要區別在於它們對當前擴展節點所采用的擴展方式不同。在分支限界法中,每一個活結點只有一次機會成為擴展節點。活結點一旦成為擴展節點,就一次性產生其所有兒子節點。在這些兒子節點中,導致不可行解或導致非最優解的兒子節點被舍棄,其余兒子節點被加入活結點表中。此后,從活結點表中取下一節點為當前擴展節點。並重復上述節點擴展過程。這個過程移至持續到找到所需的解或活結點表為空為止。
從活結點表中選擇下一擴展節點的不同方式導致不同的分支限界法。
最常見的有以下兩種方式:
(1)隊列式分支限界法
隊列式分支限界法將活結點表組織成一個隊列,並按隊列的先進先出原則選取下一個節點為當前擴展節點。
(2)優先隊列式分支限界法
優先隊列式的分支限界法將活結點表組織成一個優先隊列,並按優先隊列中規定的節點優先級選取優先級最高的下一個節點成為當前擴展節點。
2.問題分析及算法設計
問題分析:
(1)解旅行售貨員問題的優先隊列式分支限界法用優先隊列存儲活結點表。
(2)活結點m在優先隊列中的優先級定義為:活結點m對應的子樹費用下界lcost。
(3)lcost=cc+rcost,其中,cc為當前結點費用,rcost為當前頂點最小出邊費用加上剩余所有頂點的最小出邊費用和。
(4)優先隊列中優先級最大的活結點成為下一個擴展結點。
(5)排列樹中葉結點所相應的載重量與其優先級(下界值)相同,即:葉結點所相應的回路的費用(bestc)等於子樹費用下界lcost的值。
算法設計:
(1) 要找最小費用旅行售貨員回路,選用最小堆表示活結點優先隊列。
(2) 算法開始時創建一個最小堆,用於表示活結點優先隊列。
(3) 堆中每個結點的優先級是子樹費用的下界lcost值。
(4) 計算每個頂點i的最小出邊費。
(5) 如果所給的有向圖中某個頂點沒有出邊,則該圖不可能有回路,算法結束。
特例:(如下圖所示)
- 算法復雜度分析
對於分支限界法的復雜度是根據數據的不同而不同,搜索的節點越少,復雜度越低,這跟目標的選取有很大的關系,目標的函數值計算也是需要一定的時間。
三、源程序核心代碼及注釋(截圖)
四、運行結果
五、調試和運行程序過程中產生的問題及解決方法,實驗總結(5行以上)
對於這一次實驗,感覺分支限界法調試起來,起初遞歸來回跳,看的頭昏眼花的,看的雲里霧里的,后面自己去畫了個草圖,結合圖來,一步一步的調試,這里還需要判斷是否符合最優解,當在父節點的最優總權值>最優總權值時,這時就要直接剪枝,這里主要是限界條件的當前結點的走過的路徑長度<最短路徑長度,這里的最短路徑長度並不一定是最優解,而只是計算到某一步得到的最優解。這讓調試看起來,來回跳動,最終將其它子節點按優先級順序依次存入隊列,然后挑出隊列中優先級最高的節點作為下次擴展節點,然后一直重復上述的操作,最后調試完畢,就可以得到最優解。
源碼:
#include<bits/stdc++.h>
#define NO -1 //沒有通路
#define MAX_WEIGHT 4000
using namespace std;
int n; //城市數目
int City_Graph[100][100]; //保存圖信息
int x[100]; //x[i]保存第i步遍歷的城市
int isIn[100]; //保存 城市i是否已經加入路徑
int bestw; //最優路徑總權值
int cw; //當前路徑總權值
int bestx[100]; //最優路徑
void Travel_Backtrack(int t)
{
int i, j;
if (t > n) { //到達葉子節點,走完了,輸出結果
if (cw < bestw) { //判斷當前路徑是否是更優解
for (i = 1; i <= n; i++)
{
bestx[i] = x[i];//保存最優路勁
}
bestw = cw + City_Graph[x[n]][1];//最優路勁總權值
}
return;
}
else {
for (j = 1; j <= n; j++) { //找到第t步能走的城市
if (City_Graph[x[t - 1]][j] != NO && !isIn[j]) { //能到而且沒有加入到路徑中
isIn[j] = 1;//加入路勁
x[t] = j;//保存所在城市
cw += City_Graph[x[t - 1]][j];//加上當前路勁總權值
Travel_Backtrack(t + 1);//遞歸回溯
isIn[j] = 0;//還原狀態
x[t] = 0;//還原狀態
cw -= City_Graph[x[t - 1]][j];//還原當前路勁總權值
}
}
}
}
int main() {
int i;
cin >> n;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
cin >> City_Graph[i][j];
}
}
//測試遞歸法
for (i = 1; i <= n; i++) {
x[i] = 0; //表示第i步還沒有解
bestx[i] = 0; //還沒有最優解
isIn[i] = 0; //表示第i個城市還沒有加入到路徑中
}
x[1] = 1; //第一步 走城市1
isIn[1] = 1; //第一個城市 加入路徑
bestw = MAX_WEIGHT;
cw = 0;
Travel_Backtrack(2); //從第二步開始選擇城市
cout << bestw;
return 0;
}