旅行商问题回溯法求解

问题描述

某售货员要到若干城市去推销商品,已知各城市之间的路程(旅费),他要选定一条从驻地出发,经过每个城市一遍,最后回到驻地的路线,使总的路程(总旅费)最小。

在这里插入图片描述

解空间

解空间:排列树
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
*/

在这里插入图片描述

发布了97 篇原创文章 · 获赞 101 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/practical_sharp/article/details/102827406