旅行商問題回溯法求解

問題描述

某售貨員要到若干城市去推銷商品,已知各城市之間的路程(旅費),他要選定一條從駐地出發,經過每個城市一遍,最後回到駐地的路線,使總的路程(總旅費)最小。

在這裏插入圖片描述

解空間

解空間:排列樹
x=[1 2 3……n]
相應的排列樹由x[1:n]的所有排列構成

思路

旅行商問題的解空間是一棵排列樹。對於排列樹的回溯搜索與生成1,2,3…n的所有排列的遞歸算法Perm相似。
初始的時候x=[1,2,3,…n].。
在遞歸算法getmincost()中,當i==n時,當前遍歷到了排列樹的葉子節點的上一層節點(即n-1層),這個時候要判斷第n-1個點與第n個點以及第n個點與第1個點之間是否形成一條迴路,如果形成一條迴路,則可以判斷當前迴路的cost是否小於目前最優值,進而判斷是否更新最優值和最優解。

當i<n的時候,還沒有遍歷到判斷n個頂點是否形成迴路。這個時候能夠遍歷當前節點的條件是當前節點i與上一節點i-1連通(即從第一個節點一直到第i個節點形成了一條路徑),並且這條路徑的長度小於當前最優值,否則不遍歷當前節點(約束函數)。

輸入

輸入第一行爲一個整數n,表示圖的頂點數
輸入第二行爲一個整數k,表示圖的邊數
輸入第3到k+3-1行表示邊的信息,每一行三個數,分別表示頂點i,頂點j,i到j的路徑長度a[i][j]

4
6
1 2 30
1 3 6
1 4 4
2 3 5
2 4 10
3 4 20

輸出

輸出有兩行
第一行爲最優值,表示旅行商的最短路徑長度
第二行爲最優解,爲旅行商的頂點遍歷序列

25
1 3 2 4 1

代碼

#include <iostream>
using namespace std;
#include<cstring>
#include<math.h>
#define NoEdge -1
int n;//定義圖的頂點個數
int a[101][101];//定義圖的鄰接矩陣
int x[101];//定義當前解
int bestx[101];//定義當前最優解
int bestc;//定義當前當前值
int cc;//定義當前路徑長度,形成環的時候與bestc比較看能不能更新bestc
void getmincost(int i)
{
    //如果訪問到n個節點,要判斷是否形成迴路
    //如果當前值優於最優值,更新最優值和最優解
    if(i==n){
        //形成迴路的條件就是x[n-1]與x[n]連通,x[n]與x[1]連通
        if(a[x[n-1]][x[n]]!=NoEdge&&a[x[n]][1]!=NoEdge){//說明形成了迴路
            //如果當前值優於最優值,更新最優值和最優解
            //bestc=NoEdge說明還沒有廣搜到一條迴路,那就先試着求出一個可行解
            if(cc+a[x[n-1]][x[n]]+a[x[n]][1]<bestc||bestc==NoEdge){
                for(int k=2;k<=n;k++)
                    bestx[k]=x[k];
                bestc=cc+a[x[n-1]][x[n]]+a[x[n]][1];//更新最優值
            }
        }
        return ;
    }
    //當前在第i層,還得繼續尋找
    else{
        for(int j=i;j<=n;j++){
            //判斷是否可以進入x[j]子樹
            //x[i-1]與x[j]連通使得1-i層連成一條路徑且累計花費優於目前最優值
            //可以進入x[j]子樹
            //這裏要做的就是交換x[i]與x[j],進入i+1層
            //思想類似於n的全排列問題,遞歸求解
            //bestc=NoEdge說明還沒有廣搜到一條迴路,那就先試着求出一個可行解
            //現在的解是x[1],x[2]...x[i]...x[j]...x[n]
            if(a[x[i-1]][x[j]]!=NoEdge&&cc+a[x[i-1]][x[j]]<bestc||bestc==NoEdge){
                //滿足條件,可以交換
                //交換之後,現在的解是x[1],x[2]...x[j]...x[i]...x[n]
                swap(x[i],x[j]);
                //現在的解是x[1],x[2]...x[j]...x[i]...x[n]
                //此時第i個元素是==x[j]
                //第j個元素是==x[i]
                cc=cc+a[x[i-1]][x[i]];//更新路徑的長度,進入i+1層
                getmincost(i+1);
                cc=cc-a[x[i-1]][x[i]];//還原路徑的長度,比較x[j+1]子樹
                
                swap(x[i],x[j]);//還原之前的解
                //現在的解是x[1],x[2]...x[i]...x[j]...x[n]
            }
        }
    }
    return ;

}
int main()
{
    cin>>n;//輸入頂點個數
    int k;
    memset(a,NoEdge,sizeof(a));
    cin>>k;//輸入邊的個數;
    int p,q,len;
    //初始化鄰接矩陣
    for(int i=1;i<=k;i++){
        cin>>p>>q>>len;
        a[p][q]=a[q][p]=len;
    }
    //初始化最優解
    for(int i=1;i<=n;i++)
        bestx[i]=x[i]=i;
    //初始化最優值
    bestc=NoEdge;
    cc=0;//初始化當前值爲0
    getmincost(2);//出發點已知
    cout<<bestc<<endl;
    for(int i=1;i<=n;i++)
        cout<<bestx[i]<<" ";
    cout<<1;

}

/*
4
6
1 2 30
1 3 6
1 4 4
2 3 5
2 4 10
3 4 20
*/

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章