TSP問題(Traveling Salesman Problem,旅行商問題),由威廉哈密頓爵士和英國數學家克克曼T.P.Kirkman於19世紀初提出。問題描述如下:
有若干個城市,任何兩個城市之間的距離都是確定的,現要求一旅行商從某城市出發必須經過每一個城市且只在一個城市逗留一次,最后回到出發的城市,問如何事先確定一條最短的線路已保證其旅行的費用最少?
另一個類似的問題為:一個郵遞員從郵局出發,到所轄街道投郵件,最后返回郵局,如果他必須走遍所轄的每條街道至少一次,那么他應該如何選擇投遞路線,使所走的路程最短?這個描述之所以稱為中國郵遞員問題(Chinese Postman Problem CPP)
為簡化該問題,假設只有A、B、C、D四個城市,各城市的關系如圖所示,權值表示兩個城市之間的的距離。
為了將圖中關系數據化,可用如下規則來描述:
城市映射為編號:A——0,B——1,C——2,D——3;
城市之間的距離用二維數組來表示,記為D[i][j],如:D[0][1]表示城市A與城市B之間的距離,於是D[0][1]=2;
用一維數組S[i]來存儲訪問過的路徑。
該問題的基本解法為遞歸遍歷,但是會產生組合爆炸問題(時間復雜度為n!),此處不做介紹,而引入一種更為高效的解法:貪心算法。該算法描述如下:
貪心算法:又稱貪婪算法(greedy algorithm),該算法是指:在對問題求解時,總是做出當前情況下的最好選擇,否則將來可能會后悔,故名“貪心”。這是一種算法策略,每次選擇得到的都是局部最優解。選擇的策略必須具備無后效性,即某個狀態以前的過程不會影響以后的狀態,只與當前狀態有關。
針對TSP問題,使用貪心算法的求解的過程為:
1.從某一個城市開始,每次選擇一個城市,直到所有的城市被走完。
2.每次在選擇下一個城市的時候,只考慮當前情況,保證迄今為止經過的路徑總距離最小。
使用貪心算法求解TSP問題的步驟描述如圖所示:
具體代碼如下:
#include<iostream>
using namespace std;
#define n 4
int main()
{
int i,j,k,l;
int S[n];//用於存儲已訪問過的城市
int D[n][n];//用於存儲兩個城市之間的距離
int sum = 0;//用於記算已訪問過的城市的最小路徑長度
int Dtemp;//保證Dtemp比任意兩個城市之間的距離都大(其實在算法描述中更准確的應為無窮大)
int flag;////最為訪問的標志,若被訪問過則為1,從未被訪問過則為0
/*初始化*/
i = 1;//i是至今已訪問過的城市
S[0] = 0;
D[0][1] = 2;D[0][2] = 6;D[0][3] = 5;D[1][0] = 2;D[1][2] = 4;
D[1][3] = 4;D[2][0] = 6;D[2][1] = 4;D[2][3] = 2;D[3][0] = 5;
D[3][1] = 4;D[3][2] = 2;
do{
k = 1;Dtemp = 10000;
do{
l = 0;flag = 0;
do{
if(S[l] == k){//判斷該城市是否已被訪問過,若被訪問過,
flag = 1;//則flag為1
break;//跳出循環,不參與距離的比較
}else
l++;
}while(l < i);
if(flag == 0&&D[k][S[i - 1]] < Dtemp){/*D[k][S[i - 1]]表示當前未被訪問的城市k與上一個已訪問過的城市i-1之間的距離*/
j = k;//j用於存儲已訪問過的城市k
Dtemp = D[k][S[i - 1]];//Dtemp用於暫時存儲當前最小路徑的值
}
k++;
}while(k < n);
S[i] = j;//將已訪問過的城市j存入到S[i]中
i++;
sum += Dtemp;//求出各城市之間的最短距離,注意:在結束循環時,該旅行商尚未回到原出發的城市
}while(i < n);
sum += D[0][j];//D[0][j]為旅行商所在的最后一個城市與原出發的城市之間的距離
for(j = 0; j < n; j++){ //輸出經過的城市的路徑
cout<<j<<" ";
}
cout<<"\n"<<sum;//求出最短路徑的值
}
運行結果如下圖所示:
參考資料:《計算機導論》第7講-算法-程序與計算系統之靈魂