旅行商问题的手工运算及完整代码(TSP)
目錄
1.問題描述
2.解空間樹是排列樹
3.算法描述?
4.手工運算?
第一步:找出每一行的最小值
第二步:找較短路徑
第三步:比較大小?
5.代碼實現
(1)分支界限法
(2) 回溯法
1.問題描述
·某售貨員要到若干城市去推銷商品,已知各城市之間的路程(或旅費)。他要選定一條從駐地出發,經過每個城市一次,最后回到駐地的路線,使總的路程(或總旅費)最小。
2.解空間樹是排列樹
3.算法描述?
·算法開始時創建一個最小堆,用于表示活結點優先隊列。
·堆中每個活結點的優先級為:cc+lcost。cc為出發城市到當前城市的路程(或費用),lcost是當前頂點(當前城市)最小出邊+剩余頂點(城市)最小出邊和(禁忌除外)。
·每次從優先隊列中取出一個活結點成為擴展結點(s層結點)。
·當s=n-2時,擴展出的結點是排列樹中某個葉子結點的父結點。如該葉結點相應一條可行回路且費用小于當前最優解bestc,則將該結點插入到優先隊列中,否則舍去該結點。
·當s<n-2時,產生當前擴展結點的所有兒子結點。計算可行兒子結點的優先級cc+lcost及相關信息。當cc+lcost<bestc時,將這個可行兒子結點插入到活結點優先隊列中。
該擴展過程一直持續到優先隊列中取出的活結點是一個葉子結點為止。
最小出邊(禁忌除外)的解釋?
對于剛擴展出的頂點,其前已選的所有頂點是禁忌的(不能選)。對于未擴展出的頂點,其前已選的?(頂點1除外)的頂點是禁忌的。
4.手工運算?
我們先來看一道例題:
0? ?14? 4? 10? ?20
14? ?0? ?7? ?8? ? ?7
4? ? ?5? ?0? ?7? ? 16
11? ?7? ?9? ?0? ? ?2
18? ?7? 17? ?4? ? 0
第一步:找出每一行的最小值
第二步:找較短路徑
從1開始向后找
(1)第一步定位1->2,具體推導如下圖
第三步:比較大小?
30<31<42<48
故選擇路徑:1->4->5->2->3->1
5.代碼實現
(1)分支界限法
#include <iostream> #include <algorithm> #include <cstring> #include <cmath> #include <queue> using namespace std; const int INF=1e7; //設置無窮大的值為10^7 const int N=100; double g[N][N]; //景點地圖鄰接矩陣 int bestx[N]; //記錄當前最優路徑 double bestl; //當前最優路徑長度 int n,m; //景點個數n,邊數m struct Node//定義結點,記錄當前結點的解信息 {double cl; //當前已走過的路徑長度int id; //景點序號int x[N];//記錄當前路徑Node() {}Node(double _cl,int _id){cl = _cl;id = _id;} };//定義隊列的優先級。 以cl為優先級,cl值越小,越優先 bool operator <(const Node &a, const Node &b) {return a.cl>b.cl; }//Travelingbfs 為優先隊列式分支限界法搜索 double Travelingbfs() {int t; //當前處理的景點序號tNode livenode,newnode;//定義當前擴展結點livenode,生成新結點newnodepriority_queue<Node> q; //創建一個優先隊列,優先級為已經走過的路徑長度cl,cl值越小,越優先newnode=Node(0,2);//創建根節點for(int i=1;i<=n;i++){newnode.x[i]=i;//初時化根結點的解向量}q.push(newnode);//根結點加入優先隊列cout<<"按優先級出隊順序:"<<endl;//用于調試while(!q.empty()){livenode=q.top();//取出隊頭元素作為當前擴展結點livenodeq.pop(); //隊頭元素出隊//用于調試cout<<"當前結點的id值:"<<livenode.id<<"當前結點的cl值:"<<livenode.cl<<endl;cout<<"當前結點的解向量:";for(int i=1; i<=n; i++){cout<<livenode.x[i];}cout<<endl;t=livenode.id;//當前處理的景點序號// 搜到倒數第2個結點時個景點的時候不需要往下搜索if(t==n) //立即判斷是否更新最優解,//例如當前找到一個路徑(1243),到達4號結點時,立即判斷g[4][3]和g[3][1]是否有邊相連,//如果有邊則判斷當前路徑長度cl+g[4][3]+g[3][1]<bestl,滿足則更新最優值和最優解{//說明找到了一條更好的路徑,記錄相關信息if(g[livenode.x[n-1]][livenode.x[n]]!=INF&&g[livenode.x[n]][1]!=INF)if(livenode.cl+g[livenode.x[n-1]][livenode.x[n]]+g[livenode.x[n]][1]<bestl){bestl=livenode.cl+g[livenode.x[n-1]][livenode.x[n]]+g[livenode.x[n]][1];cout<<endl;cout<<"當前最優的解向量:";for(int i=1;i<=n;i++){bestx[i]=livenode.x[i];cout<<bestx[i];}cout<<endl;cout<<endl;}continue;}//判斷當前結點是否滿足限界條件,如果不滿足不再擴展if(livenode.cl>=bestl)continue;//擴展//沒有到達葉子結點for(int j=t; j<=n; j++)//搜索擴展結點的所有分支{if(g[livenode.x[t-1]][livenode.x[j]]!=INF)//如果x[t-1]景點與x[j]景點有邊相連{double cl=livenode.cl+g[livenode.x[t-1]][livenode.x[j]];if(cl<bestl)//有可能得到更短的路線{newnode=Node(cl,t+1);for(int i=1;i<=n;i++){newnode.x[i]=livenode.x[i];//復制以前的解向量}swap(newnode.x[t], newnode.x[j]);//交換x[t]、x[j]兩個元素的值q.push(newnode);//新結點入隊}}}}return bestl;//返回最優值。 }void init()//初始化 {bestl=INF;for(int i=0; i<=n; i++){bestx[i]=0;}for(int i=1;i<=n;i++)for(int j=i;j<=n;j++)g[i][j]=g[j][i]=INF;//表示路徑不可達 } void print()//打印路徑 {cout<<endl;cout<<"最短路徑: ";for(int i=1;i<=n;i++)cout<<bestx[i]<<"--->";cout<<"1"<<endl;cout<<"最短路徑長度:"<<bestl; }int main() {int u, v, w;//u,v代表城市,w代表u和v城市之間路的長度cout << "請輸入景點數 n(結點數):";cin >> n;init();cout << "請輸入景點之間的連線數(邊數):";cin >> m;cout << "請依次輸入兩個景點u和v之間的距離w,格式:景點u 景點v 距離w:"<<endl;for(int i=1;i<=m;i++){cin>>u>>v>>w;g[u][v]=g[v][u]=w;}Travelingbfs();print();return 0; }(2) 回溯法
#include <iostream> #include <cstring> #include <algorithm> using namespace std; const int INF=1e7; const int N=100; int g[N][N]; int x[N]; //記錄當前路徑 int bestx[N]; //記錄當前最優路徑 int cl; //當前路徑長度 int bestl; //當前最短路徑長度 int n,m; //城市個數n,邊數m void Traveling(int t) {if(t>n){ //到達葉子結點//推銷貨物的最后一個城市與住地城市有邊相連并且路徑長度比當前最優值小//說明找到了一條更好的路徑,記錄相關信息if(g[x[n]][1]!=INF && (cl+g[x[n]][1]<bestl)){for(int j=1;j<=n;j++)bestx[j]=x[j];bestl=cl+g[x[n]][1];}}else{//沒有到達葉子結點for(int j=t; j<=n; j++){//搜索擴展結點的所有分支//如果第t-1個城市與第t個城市有邊相連并且有可能得到更短的路線if(g[x[t-1]][x[j]]!=INF&&(cl+g[x[t-1]][x[j]]<bestl)){//保存第t個要去的城市編號到x[t]中,進入到第t+1層swap(x[t], x[j]);//交換兩個元素的值cl=cl+g[x[t-1]][x[t]];Traveling(t+1); //從第t+1層的擴展結點繼續搜索//第t+1層搜索完畢,回溯到第t層cl=cl-g[x[t-1]][x[t]];swap(x[t], x[j]);}}} } void init()//初始化 {bestl=INF;cl=0;for(int i=1;i<=n;i++)for(int j=i;j<=n;j++)g[i][j]=g[j][i]=INF;//表示路徑不可達for(int i=0; i<=n; i++){x[i]=i;bestx[i]=0;} } void print()//打印路徑 { // cout<<"最短路徑: "; // for(int i=1;i<=n; i++) // cout<<bestx[i]<<"--->"; // cout<<"1"<<endl;cout<<bestl<<endl; } int main() {int t;cin>>t;while(t--){int u, v, w;//u,v代表城市,w代表u和v城市之間路的長度 // cout << "請輸入景點數 n(結點數):";cin >> n;init(); // cout << "請輸入景點之間的連線數(邊數):";cin >> m; // cout << "請依次輸入兩個景點u和v之間的距離w,格式:景點u 景點v 距離w";for(int i=1;i<=m;i++){cin>>u>>v>>w;g[u][v]=g[v][u]=w;}Traveling(2);print();}return 0; }?希望對大家的學習有所幫助。
總結
以上是生活随笔為你收集整理的旅行商问题的手工运算及完整代码(TSP)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 充分挖掘信访大数据的价值
- 下一篇: Cisco Packet Tracer软