旅行商问题之分支界限法(bfs)

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
using namespace std;
const int INF=1e7;//正无穷
const int N=100;//最大地点容量
double graph[N][N];//地点邻接矩阵
int bestRoad[N];//最优路径
double bestlong;//最优路径长度
int n,m;

//排列树节点
struct Node{
    double nowlong;//走过的长度
    int index;//正在寻找第几个景点
    int road[N];//路径
    Node(){

    }
    Node(double _nowlong,int _index){
        nowlong=_nowlong;
        index=_index;//排列树层数
    }
};

//定义优先队列优先级:nowlong越小越优先
bool operator < (const Node&a,const Node&b){
    return a.nowlong>b.nowlong;
}


void init();//初始化
double bfs();//优先队列bfs
void print();//打印解


//主函数
int main(void){
    int u,v,w;//u---v u,v为地点 w为二者之间的距离
    cout<<"请输入景点的个数(即无向图节点个数)\n";
    cin>>n;
    init();//初始化
    cout<<"请输入景点之间的边数\n";
    cin>>m;
    cout<<"请依次输入u v w,u:顶点 v:顶点 w:v--v距离\n";
    for(int i=1;i<=m;i++){
        cin>>u>>v>>w;
        graph[u][v]=graph[v][u]=w;
    }
    //广度优先求最优
    bfs();
    //输出最优解
    print();
    return 0;
}

//初始化
void init(){
    //初始化最优路径长度:正无穷,因为要迭代求最优
    bestlong=INF;
    //初始化解向量
    for(int i=0;i<=n;i++){
        bestRoad[i]=0;
    }
    //初始化邻接矩阵
    for(int i=0;i<=n;i++){
        for(int j=i;j<=n;j++){
            graph[i][j]=graph[j][i]=INF;//默认任意两点不可达
        }
    }
}

//打印解
void print(){
    cout<<"最优路径:";
    for(int i=1;i<=n;i++){
        cout<<bestRoad[i]<<"--->";
    }
    cout<<1<<endl;
    cout<<"最优路径长度:"<<bestlong<<endl;
}


//排列树
/*
 *                  *(node 0)
 *                  |1
 *                  *(根节点)
 *          |2      |3       |4
 *          *       *        *
 *       |3    |4 |2  |4   |2  |3
 *       *     *  *   *    *   *
 *       |4    |3 |4  |2   |3  |2
 *       *     *  *   *    *   *
 * */

//优先队列 bfs:默认从景点1出发
double bfs(){
    //当前所在排列树的层数
    int nowStep;
    Node liveNode;//当前扩展节点
    Node newNode;//生成新节点
    //创建优先队列
    priority_queue<Node> nodeQueue;
    //创建根节点
    newNode=Node(0,2);
    for(int i=1;i<=n;i++){//初始化根节点的解向量
        newNode.road[i]=i;
    }
    //根节点加入队列
    nodeQueue.push(newNode);

    while(!nodeQueue.empty()){
        liveNode=nodeQueue.top();//最队头作为扩展节点
        nodeQueue.pop();
        nowStep=liveNode.index;//当前处理景区序号:开始时nowStep=2 因为第1层默认选景点1,直接跳过起点
        if(nowStep==n){//到达了排列树倒数第二层时
            //判断是不是解,是不是更优解
            //判断当前节点到对应的叶子节点之间是否有路径 判断叶子节点到根节点是否有路径(因为要旅游一圈回到起点)
            if(graph[liveNode.road[n-1]][liveNode.road[n]]!=INF&&graph[liveNode.road[n]][1]!=INF){
                //判断是否是更优解
                if(liveNode.nowlong+graph[liveNode.road[n-1]][liveNode.road[n]]+graph[liveNode.road[n]][1]<bestlong){
                    //迭代最优解长度
                    bestlong=liveNode.nowlong+graph[liveNode.road[n-1]][liveNode.road[n]]+graph[liveNode.road[n]][1];
                    //记录最优解的解向量
                    for(int i=1;i<=n;i++){
                        bestRoad[i]=liveNode.road[i];
                    }
                }
                continue;//结束队当前扩展节点的操作,是倒数第二层节点不用将叶子节点在入队列了,没必要
            } 
        }

        //判断是否满足扩展界限条件
        //不扩展
        if(liveNode.nowlong>=bestlong){//此方案,从起点到当前扩展节点的距离已经没有最优解优了
            continue;
        }
        //扩展
        //生成扩展节点的所有分支
        for(int j=nowStep;j<=n;j++){
            //如果扩展节点与分支节点之间有路径
            if(graph[liveNode.road[nowStep-1]][liveNode.road[nowStep]]!=INF){
                double templong=liveNode.nowlong+graph[liveNode.road[nowStep-1]][liveNode.road[j]];
                if(templong<bestlong){//界限条件
                    newNode=Node(templong,nowStep+1);
                    for(int i=1;i<=n;i++){//复制以前的解向量
                        newNode.road[i]=liveNode.road[i];
                    }
                    swap(newNode.road[nowStep],newNode.road[j]);//交换road[nowStep]与road[j]
                    //新节点入队列
                    nodeQueue.push(newNode);
                }
            }
        }
    }
    return bestlong;
}

测试样例

请输入景点的个数(即无向图节点个数)
4
请输入景点之间的边数
6
请依次输入u v w,u:顶点 v:顶点 w:v--v距离
1 2 15
1 3 30
1 4 5
2 3 6
2 4 12
3 4 3
最优路径:1--->4--->3--->2--->1
最优路径长度:29```