用動態規劃方法旅行商問題(TSP問題)
為各城市間的距離矩陣。
問:該推銷員應如何選擇路線,才能使總的行程最短?
以下是用動態規劃方法,Linux下g++編譯通過 #include <iostream>
#include <set>
#include <vector>
#define MAX 6
usingnamespace std;
int dis[MAX][MAX]={
0, 10, 20, 30, 40, 50,
12, 0 ,18, 30, 25, 21,
23, 19, 0, 5, 10, 15,
34, 32, 4, 0, 8, 16,
56, 22, 16,20, 12, 0
};
typedef struct
{
int curcity;//當前所在的城市
vector<int> unvisited;//當前未訪問的城市
set<int> type;//由於set自動排序,相同狀態的vector可能不同,但set必然相同
int distance;//從當前城市到終點回到起點的距離
}status;
/*測試用*/
void printVec( vector<status> vec)
{
vector<status>::iterator iter;
for(iter=vec.begin();iter!=vec.end();iter++)
{
cout<<(*iter).curcity<<" <";
for(it=(*iter).unvisited.begin();it!=(*iter).unvisited.end();it++)
{
cout<<*it<<"";
}
cout<<"
}
}
//看看當前狀態的城市中是否包括城市i
bool contain(int i, status &sta)
{
vector<int>::iterator iter;
if(i==sta.curcity)
returntrue;
else
{
for(iter=sta.unvisited.begin();iter!=sta.unvisited.end();iter++)
if(i==*iter)
returntrue;
}
returnfalse;
}
/*合併相同狀態*/
vector<status> combine(vector<status> vec)
{
vector<status> new_vec;
vector<status>::iterator iter;
status temp;
while(vec.size()>0)
{
iter=vec.begin();
temp=*iter;
vec.erase(iter);
for(;iter!=vec.end();iter++)
{
if((temp.curcity==(*iter).curcity)&&(temp.type==(*iter).type))
{
if((*iter).distance<temp.distance)
temp=*iter;
iter=vec.erase(iter);
iter--;
}
}
new_vec.push_back(temp);
}
return new_vec;
}
int main()
{
vector<status> pre_vector;
vector<status> cur_vector;
//從後往前推,初始化
for(int i=1;i<MAX;i++)
{
status sta;
sta.curcity=i;
sta.distance=dis[i][0];
cur_vector.push_back(sta);
}
//依次遞推,遞推MAX-2次
for(int j=0;j<MAX-2;j++){
pre_vector=cur_vector;
cur_vector.clear();
for(int i=1;i<MAX;i++)
{
vector<status>::iterator iter;
for(iter=pre_vector.begin();iter!=pre_vector.end();iter++)
{
status temp=*iter;
if(contain(i,temp)==false)//為確保狀態中沒有重複路徑
{
status new_stat=temp;
vector<int>::iterator int_iter=new_stat.unvisited.begin();
new_stat.unvisited.insert(int_iter,new_stat.curcity);//加入vector
new_stat.type.insert(new_stat.curcity);//加入set
new_stat.distance+=dis[i][new_stat.curcity];//計算距離
new_stat.curcity=i;
cur_vector.push_back(new_stat);
}
}
}
//記錄相同狀態最短路徑,併合並相同狀態
cur_vector=combine(cur_vector);
}//end for
//printVec(cur_vector);
//遞推完畢後,最後一步,計算起點到每個狀態的距離,找到最短路徑
vector<status>::iterator iter=cur_vector.begin();
status shortest=*iter;
int min_dis=shortest.distance+dis[0][shortest.curcity];
iter++;
for(;iter!=cur_vector.end();iter++)
{
int temp_dis=dis[0][(*iter).curcity]+(*iter).distance;
if(temp_dis<min_dis)
{
min_dis=temp_dis;
shortest=*iter;
}
}
//列印結果
vector<int>::iterator iter_city;
cout<<"minimum distance is "<<min_dis<<endl;
cout<<"the shortest path is "<<"1 "<<shortest.curcity+1;
for(iter_city=shortest.unvisited.begin();iter_city!=shortest.unvisited.end();iter_city++)
cout<<""<<*iter_city+1;
cout<<" 1"<<endl;
return0;
}
執行結果如下
minimum distance is 80
the shortest path is 12 6 5 4 3 1
注意:動態規劃方法並不是解決TSP問題的一個好方法,因其佔用空間和時間複雜度均較大。
相關資料(從網上摘抄,注意所舉例子的資料與程式不同):
設有n個城市, 其中每兩個城市之間都有道路相連,城市i和城市j之間的距離為Cij。從某城市出發周遊所有城市,經過每個城市一次且僅一次,最後回到出發地,求總行程最短的周遊路線。對於一般的情況可以假設兩城市之間往返距離不相等。在此例中,為了簡化問題,設往返距離相等,即Cij=Cji。
這就是所謂的貨郎擔問題(Traveling Salesman Problem,簡稱TSP)。這個問題與最短路徑問題不同,最短路徑問題以當前所在的位置作為狀態變數,而在貨郎擔問題中,狀態變數除了要指明當前所在位置外,還要指明已經經過哪幾個城市。
由於貨郎擔問題經過的路線是一條經過所有城市的閉合迴路,因此從哪一點出發是無所謂的,因此不妨設從城市1出發。
問題的動態規劃模型構造如下:
階段k:已經歷過的城市個數,包括當前所在的城市。k=1, 2, …, n , n+1,k=1表示出發時位於起點,k=n+1表示結束時回到終點。
狀態變數:xk=(i, Sk),其中i表示當前所在的城市,Sk表示尚未訪問過的城市的集合。很明顯
S1={2,3,…,n},…,Sn=Sn+1=F
其中F表示空集。並且有
xn=(i, F) i=2,3,…,n, xn+1=(1, F)
決策變數:dk=( i , j ),其中i為當前所在的城市,j為下一站將要到達的城市。
狀態轉移方程:若當前的狀態為
xk=( i ,Sk)
採取的決策為
dk=( i , j )
則下一步到達的狀態為
xk+1=T(xk,dk)=( j ,Sk/ {j})
階段指標:vk(xk,dk)=Cij
最優指標函式:fk(xk)=fk(i,Sk) 表示從城市i出發,經過Sk中每個城市一次且僅一次,最後返回城市1的最短距離。
終端條件:fn+1(xn+1)=fn+1(1, F)=0
對於如圖3.7.1所示的一個五個城市的貨郎擔問題,求解步驟如下:
對於k=5,有
f5(i, F)=min{Cij+f6(1, F)}=Ci1 i=2,3,4,5
d5Î(i,1)
f5(I,F)的值列表如下:
i f5(i, F)
2 2
3 7
4 2
5 5
對於k=4,有
f4(i, S4)=min{Cij+f5(j,S5)}
jÎS4
f4(i,S4)的值列表如下:
(i,S4) j Cij S5 Cij+f5(j,S5) f4(i,S4) j*
(2,{3}) {3} 3 F 3+f5(3,F)=3+7=10 10 3
(2,{4}) {4} 5 F 5+f5(4,F)=5+2=7 7 4
(2,{5}) {5} 1 F 1+f5(5,F)=1+5=6 6 5
(3,{2}) {2} 3 F 3+f5(2,F)=3+2=5 5 2
(3,{4}) {4} 4 F 4+f5(4,F)=4+2=6 6 4
(3,{5}) {5} 6 F 6+f5(5,F)=6+5=11 11 5
(4,{2}) {2} 5 F 5+f5(2,F)=5+2=7 7 2
(4,{3}) {3} 4 F 4+f5(3,F)=4+7=11 11 3
(4,{5}) {5} 3 F 3+f5(5,F)=3+5=8 8 5
(5,{2}) {2} 1 F 1+f5(2,F)=1+2=3 3 2
(5,{3}) {3} 6 F 6+f5(3,F)=6+7=13 13 3
(5,{4}) {4} 3 F 3+f5(4,F)=3+2=5 5 4
對於k=3,有
f3(i,S3)=min{Cij+f4(j,S4)}
jÎS3
f3(i,S3)的值列表如下:
(i,S3) j Cij S4 Cij+f4(j,S4) f3(i,S3) j*
(2,{3,4}) {3}{4} 35 {4}{3} 3+f4(3,{4})=3+6=9*5+f4(4,{3})=5+11=16 9 3
(2,{3,5}) {3}{5} 31 {5}{3} 3+f4(3,{5})=3+11=14*1+f4(5,{3})=1+13=14* 14 3,5
(2,{4,5}) {4}{5} 51 {5}{4} 5+f4(4,{5})=5+8=131+f4(5,{4})=1+5=6* 6 5
(3,{2,4}) {2}{4} 34 {4}{2} 3+f4(2,{4})=3+7=10*4+f4(4,{2})=4+7=11 10 2
(3,{2,5}) {2}{5} 36 {5}{2} 3+f4(2,{5})=3+6=9*6+f4(5,{2})=6+3=9* 9 2,5
(3,{4,5}) {4}{5} 46 {5}{4} 4+f4(4,{5})=4+8=126+f4(5,{4})=6+5=11* 11 5
(4,{2,3}) {2}{3} 54 {3}{2} 5+f4(2,{3})=5+10=154+f4(3,{2})=4+5=9* 9 3
(4,{2,5}) {2}{5} 53 {5}{2} 5+f4(2,{5})=5+6=113+f4(5,{2})=3+3=6* 6 5
(4,{3,5}) {3}{5} 43 {5}{3} 4+f4(3,{5})=4+11=15*3+f4(5,{3})=3+13=16 15 3
(5,{2,3}) {2}{3} 16 {3}{2} 1+f4(2,{3})=1+10=11*6+f4(3,{2})=6+5=11* 11 2,3
(5,{2,4}) {2}{4} 13 {4}{2} 1+f4(2,{4})=1+7=8*3+f4(4,{2})=3+7=10 8 2
(5,{3,4}) {3}{4} 63 {4}{3} 6+f4(3,{4})=6+6=12*3+f4(4,{3})=3+11=14 12 3
對於k=2有
(i,S2) j Cij S3 Cij+f3(j,S3) f2(i,S2) j*
(2,{3,4,5}) {3}{4}{5} 351 {4,5}{3,5}{3,4} 3+f3(3,{4,5})=3+11=145+f3(4,