旅行商问题,bfs + 优先队列

心得:
dfs 转 bfs 的要点:

  1. 在dfs中控制回溯的分支的循环中,将向下层递归的语句改为压入队列(一般借助结构体保存各种环境信息)。
  2. bfs使用优先队列可以剪掉大部分的无用分支。
//图的结点没有出边则表示没回路
//优先队列可以剪掉大部分的无用分支
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#include<iostream>
using namespace std;
#define N 10
struct Node{
    int v;
    int len;
    int x[N];
    int rl;
    int sl;
    Node(int v_ = 0,int len_ = 0,int rl_ = 0):v(v_),len(len_),rl(rl_){}
    bool operator <(const Node& t1) const{
        return this ->sl > t1.sl;  //< 大值优先,  '>'小值优先
    }
};
int n,e;
int m[N][N];
int bestx[N];
int bestl;
int minlen[N];
int mintotal;
void bfs(int setup){
    priority_queue<Node> q;
    Node newnode,t;
    for(int i = 1; i <= n; i++){
        newnode.x[i] = i;
    }
    newnode.v = 2;
    newnode.len = 0;
    newnode.rl = mintotal;
    newnode.sl = mintotal;
    q.push(newnode);
    while(!q.empty()){
        t = q.top();
        q.pop();
        int floor = t.v;
        if(t.v == n + 1){
            if(m[t.x[floor - 1]][setup] != INT_MAX){
                bestl = t.len + m[t.x[floor - 1]][setup];
                for(int i = 1; i <= n; i++){
                    bestx[i] = t.x[i];
                }
                bestx[n + 1] = setup;
                return ;
            }
        }
        for(int i = floor; i<= n; i++){
            if(m[t.x[floor - 1]][t.x[i]] != INT_MAX && t.sl < bestl){
                newnode.v = floor + 1;
                newnode.len = t.len + m[t.x[floor - 1]][t.x[i]];
                newnode.rl -= minlen[t.x[i]];
                newnode.sl = newnode.rl + newnode.len;
                for(int j = 1; j <= n; j++){
                    newnode.x[j] = t.x[j];
                }
                swap(newnode.x[i],newnode.x[floor]);
                q.push(newnode);
            }
        }
    }
}
int main(){
    cin >> n >> e;
    for(int i = 0; i <= n; i++){
        minlen[i] = INT_MAX;
        for(int j = 0; j <= n; j++){
            m[i][j] = INT_MAX;
        }
    }
    for(int i = 0; i < e; i++){
        int v,u,len;
        cin >> v >> u >> len;
        m[v][u] = len;
        m[u][v] = len;
        minlen[v] = min(minlen[v], m[v][u]);
        minlen[u] = min(minlen[u], m[u][v]);
    }
    for(int i = 1; i <= n; i++){
        mintotal += minlen[i];
    }
    bestl = INT_MAX;
    bfs(1);
    for(int i = 1; i <= n + 1; i++){
        cout << bestx[i] << " ";
    }
    cout << endl;
    cout << bestl << endl;
}

/*
4 6
1 2 15
1 3 30
1 4 5
2 3 6
2 4 12
3 4 3
*/

旅行商问题是一个经典的组合优化问题,其目的是找到一条路径,使得经过所有城市恰好一次的总路程最小。利用分支界限法可以很好地解决该问题。 以下是C++代码实现: ```c++ #include <iostream> #include <algorithm> #include <queue> #include <vector> using namespace std; const int INF = 0x3f3f3f3f; // 无穷大 const int MAXN = 20; // 最大城市数 int n; // 总城市数 int e[MAXN][MAXN]; // 邻接矩阵 int ans = INF; // 最小路径长度 int vis[MAXN]; // 标记城市是否已访问 struct Node { int cur; // 当前城市编号 int dis; // 当前路径长度 int v[MAXN]; // 标记城市是否已访问 bool operator<(const Node &a) const { // 重载小于运算符,用于优先队列排序 return dis > a.dis; } }; void dfs(int cur, int dis, int cnt) { // 普通 DFS if (cnt == n && e[cur][1] != INF) { // 若所有城市已访问完且最后一个城市与起点有通路,更新答案 ans = min(ans, dis + e[cur][1]); return; } for (int i = 2; i <= n; i++) { // 枚举下一个城市 if (vis[i] == 0 && e[cur][i] != INF && dis + e[cur][i] < ans) { // 若该城市未访问且与当前城市有通路,且当前路径长度小于当前最优解 vis[i] = 1; // 标记该城市已访问 dfs(i, dis + e[cur][i], cnt + 1); // 继续搜索 vis[i] = 0; // 回溯,将该城市标记为未访问 } } } void bfs() { // 分支界限法 priority_queue<Node> q; // 优先队列,用于存储状态节点 Node start; start.cur = 1; start.dis = 0; start.v[1] = 1; q.push(start); // 将起始状态节点入队 while (!q.empty()) { Node u = q.top(); q.pop(); if (u.cur == 1 && u.dis == ans) { // 若搜索到起始节点且当前路径长度等于当前最优解,结束搜索 break; } for (int i = 2; i <= n; i++) { // 枚举下一个城市 if (u.v[i] == 0 && e[u.cur][i] != INF) { // 若该城市未访问且与当前城市有通路 Node v; v.cur = i; v.dis = u.dis + e[u.cur][i]; for (int j = 1; j <= n; j++) { // 将标记数组复制到新状态节点中 v.v[j] = u.v[j]; } v.v[i] = 1; // 标记该城市已访问 q.push(v); // 将新状态节点入队 } } } cout << ans << endl; // 输出最优解 } int main() { cin >> n; for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) { cin >> e[i][j]; if (e[i][j] == 0) { // 若两个城市之间没有通路,则将其距离设为无穷大 e[i][j] = INF; } } } // dfs(1, 0, 1); // 普通 DFS bfs(); // 分支界限法 return 0; } ``` 代码中包含两种方法,一种是普通的 DFS,一种是利用优先队列实现的分支界限法。 其中,优先队列可以用来存储状态节点,每次从队列中取出路径长度最短的状态节点进行扩展,避免了不必要的搜索,提高了效率。利用优先队列实现的分支界限法是一种比较常用的解决旅行商问题的方法。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值