旅行商问题 算法设计 分支限界法

四、实验内容
一个商品推销员要去若干个城市推销商品,该推销员从一个城市出发,需要经过所有城市后,回到出发地。应如何选择行进路线,以使总的行程最短。
五、算法描述及实验步骤
1.关于分支限界,本次选用了优先队列,通过重写排序函数来对队列的优先级进行定义,从而每次在队头得到自己想要的数据。
2.关于存图,在一开始打算使用邻接表,后来发现这类题型一般都是每个城市之间都有直接通路,所涉及的条数比较多,属于稠密图,所以后来改成了邻接矩阵进行存图。
3.关于剪枝函数,是考虑了顶点的费用下界,当前费用加上还没有考虑到的点最小出边费用之和,当遍历到某个点发现该点的费用下界比全局最优值大的时候就进行剪枝,剪掉该子树。
4.关于更新最优值,当更新了该顶点之后还差一个就遍历完全部点时,这个点的最优值(费用下界)为当前费用加上该点到最后一个点的费用再加上最后一个点到初始点的费用。求出值之后,如果该值比全局的最优值还要小,则更新最优值,这样就不用考虑到叶子节点了。
5.关于while循环的终止,当优先队列的头节点为次叶子节点(即叶子节点的父节点)时,就是此时的最优值就是答案,终止循环,输出路径。

代码仅供参考

#include <bits/stdc++.h>

using namespace std;
const int maxn=1e2+5;
const int inf=0x3f3f3f3f;
typedef pair<int,int> pii;
int n,m;
int minout[maxn];//顶点i的最小费用出边
int mp[maxn][maxn];
int sum;//所有顶点的最小费用出边之和。
int bestc=inf;//最优结果
/*

4
-1 30 6 4
30 -1 5 10
6 5 -1 20
4 10 20 -1

*/
struct node
{
    int lcost;//费用下界 当前节点费用 加上 总的最小费用减去上一次路径中的顶点费用之和
    int s;//路径中有多少个顶点
    int sumz;//当前结点的费用
    vector<int>x;//存放路径
    int sumx;//当前路径中的顶点的最小出边费用之和
    int far;//父节点
};

struct cmp1
{
    bool operator ()(node a, node b)
    {
        return a.sumx>b.sumx;
    }
};
void solve(int u)
{
    node start;
    start.sumx=minout[u];
    start.sumz=0;
    start.lcost=start.sumz+sum-0;
    start.x.push_back(u);
    start.s=1;
    start.far=-1;

    priority_queue<node,vector<node>,cmp1>pq;
    pq.push(start);
    while(!pq.empty())
    {
        auto temp=pq.top();
        pq.pop();
        if(temp.s==n-1)
        {
            bestc=temp.lcost;
            cout<<bestc<<endl;
//            cout<<"路径:"<<endl;
            vector<int>falg(n,0);
            for(int i=0; i<temp.s; i++)
            {
                cout<<temp.x[i]<<" ";
                falg[temp.x[i]]=1;
            }
            for(int i=1; i<=n; i++)
            {
                if(falg[i]==0)
                {
                    cout<<i<<" 1"<<endl;
                }
            }
            break;
        }

        u=temp.x[temp.s-1];

        for(int v=1;v<=n;v++)
        {

            if(v==u)continue;
            int fff=0;
            for(int i=0; i<temp.s; i++)
            {
                if(temp.x[i]==v)
                {
                    fff=1;
                    break;
                }
            }
            if(fff)
                continue;
            int w=mp[u][v];
            node next;
            next.s=temp.s+1;
            next.x=temp.x;
            next.x.push_back(v);
            next.far=u;

            next.sumz=temp.sumz+w;
            next.sumx=temp.sumx+minout[v];
            if(next.s<n-1)
            {
                next.lcost=next.sumz+sum-temp.sumx;
                if(bestc>next.lcost)  //更新
                {
                    pq.push(next);
                }
            }
            else
            {
                int t=0;
                for(int i=0; i<next.s; i++)
                {
                    t+=next.x[i];
                }
                t=(1+n)*n/2-t;
                //cout<<"t:"<<t<<endl;
                next.lcost=next.sumz+mp[v][t]+mp[t][1];
                //cout<<"mp[v][t]"<<mp[v][t]<<endl;
                if(bestc>next.lcost)
                {//cout<<next.lcost<<endl;
                    bestc=next.lcost;
                    pq.push(next);

                }
            }
        }
    }
}

int main()
{
    cin>>n;
    for(int i=1; i<=n; i++)
    {
        minout[i]=inf;
    }
    for(int i=1; i<=n; i++)
        for(int j=1; j<=n; j++)
        {
            cin>>mp[i][j];
            if(mp[i][j]!=-1)
            {
                minout[i]=min(minout[i],mp[i][j]);
            }
        }

    for(int i=1; i<=n; i++)
        sum+=minout[i];
    //cout<<sum<<endl;
    solve(1);
    return 0;
}

  • 0
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值