旅行推销员问题(英语:Travelling salesman problemTSP)是这样一个问题:给定一系列城市和每对城市之间的距离,求解访问每一座城市一次并回到起始城市的最短回路。它是组合优化中的一个NP困难问题,在运筹学和理论计算机科学中非常重要。

分支限界法在上一篇Blog中我有简单说明,并给出了基于分支界限法的Dijkstra ,这篇文章里介绍一下基于分支限界法的TSP算法。

对于TSP,我们需要利用上界和下界来对BFS进行剪枝,通过不断更新上界和下界,尽可能的排除不符合需求的child,以实现剪枝。最终,当上限和下限等同时,我们可以获得最优的BFS解,以解决TSP问题。

在第一篇中,我们用dfs获取上界,用每行矩阵最小值来获取下界。

代码如下,下面代码中,我采用贪心法(使用DFS暴力搜索到一个结果)来获取最初的上界,通过累加每行旅行商矩阵中的最小值来获取一个下界。

//分支限界法
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<queue>
const int INF = ;
const int MAX_N = ;
using namespace std;
//n*n的一个矩阵
int n;
int cost[MAX_N][MAX_N];//最少3个点,最多MAX_N个点
struct Node
{
bool visited[MAX_N];//标记哪些点走了
int s;//第一个点
int s_p;//第一个点的邻接点
int e;//最后一个点
int e_p;//最后一个点的邻接点
int k;//走过的点数
int sumv;//经过路径的距离
int lb;//目标函数的值(目标结果)
bool operator <(const Node &p)const
{
return p.lb < lb;//目标函数值小的先出队列
}
};
priority_queue<Node> pq;//创建一个优先队列
int low, up;//下界和上界
bool dfs_visited[MAX_N];//在dfs过程中搜索过 //确定上界,利用dfs(属于贪心算法),贪心法的结果是一个大于实际值的估测结果
int dfs(int u, int k, int l)//当前节点,目标节点,已经消耗的路径
{
if (k == n) return l + cost[u][];//如果已经检查了n个节点,则直接返回路径消耗+第n个节点回归起点的消耗
int minlen = INF, p;
for (int i = ; i <= n; i++)
{
if (!dfs_visited[i] && minlen > cost[u][i])//取与所有点的连边中最小的边
{
minlen = cost[u][i];//找出对于每一个节点,其可达节点中最近的节点
p = i;
}
}
dfs_visited[p] = true;//以p为下一个节点继续搜索
return dfs(p, k + , l + minlen);
}
void get_up()
{
dfs_visited[] = true;//以第一个点作为起点
up = dfs(, , );
}
//用这种简单粗暴的方法获取必定小于结果的一个值
void get_low()
{
//取每行最小值之和作为下界
low = ;
for (int i = ; i <= n; i++)
{
//创建一个等同于map的临时数组,可用memcpy
int tmpA[MAX_N];
for (int j = ; j <= n; j++)
{
tmpA[j] = cost[i][j];
}
sort(tmpA + , tmpA + + n);//对临时的数组进行排序
low += tmpA[];
}
}
int get_lb(Node p)
{
int ret = p.sumv * ;//路径上的点的距离的二倍
int min1 = INF, min2 = INF;//起点和终点连出来的边
for (int i = ; i <= n; i++)
{
//cout << p.visited[i] << endl;
if (!p.visited[i] && min1 > cost[i][p.s])
{
min1 = cost[i][p.s];
}
//cout << min1 << endl;
}
ret += min1;
for (int i = ; i <= n; i++)
{
if (!p.visited[i] && min2 > cost[p.e][i])
{
min2 = cost[p.e][i];
}
//cout << min2 << endl;
}
ret += min2;
for (int i = ; i <= n; i++)
{
if (!p.visited[i])
{
min1 = min2 = INF;
for (int j = ; j <= n; j++)
{
if (min1 > cost[i][j])
min1 = cost[i][j];
}
for (int j = ; j <= n; j++)
{
if (min2 > cost[j][i])
min2 = cost[j][i];
}
ret += min1 + min2;
}
}
return (ret + ) / ;
} int solve()
{
//贪心法确定上界
get_up();
//取每行最小的边之和作为下界
//cout << up << endl;//test
get_low();
//cout << low << endl;//test
//设置初始点,默认从1开始
Node star;
star.s = ;//起点为1
star.e = ;//终点为1
star.k = ;//走过了1个点
for (int i = ; i <= n; i++)
{
star.visited[i] = false;
}
star.visited[] = true;
star.sumv = ;//经过的路径距离初始化
star.lb = low;//让目标值先等于下界
int ret = INF;//ret为问题的解
pq.push(star);//将起点加入队列
while (pq.size())
{ Node tmp = pq.top();pq.pop();
if (tmp.k == n - )//如果已经走过了n-1个点
{
//找最后一个没有走的点
int p;
for (int i = ; i <= n; i++)
{
if (!tmp.visited[i])
{
p = i;//让没有走的那个点为最后点能走的点
break;
}
}
int ans = tmp.sumv + cost[p][tmp.s] + cost[tmp.e][p];//已消耗+回到开始消耗+走到P的消耗
//如果当前的路径和比所有的目标函数值都小则跳出
if (ans <= tmp.lb)
{
ret = min(ans, ret);
break;
}
//否则继续求其他可能的路径和,并更新上界
else
{
up = min(up, ans);//上界更新为更接近目标的ans值
ret = min(ret, ans);
continue;
}
}
//当前点可以向下扩展的点入优先级队列
Node next;
for (int i = ; i <= n; i++)
{
if (!tmp.visited[i])
{
//cout << "test" << endl;
next.s = tmp.s;//沿着tmp走到next,起点不变
next.sumv = tmp.sumv + cost[tmp.e][i];//更新路径和
next.e = i;//更新最后一个点
next.k = tmp.k + ;//更新走过的顶点数
for (int j = ; j <= n; j++) next.visited[j] = tmp.visited[j];//tmp经过的点也是next经过的点
next.visited[i] = true;//自然也要更新当前点
//cout << next.visited[i] << endl;
next.lb = get_lb(next);//求目标函数
//cout << next.lb << endl;
if (next.lb > up) continue;//如果大于上界就不加入队列
pq.push(next);//否则加入队列
//cout << "test" << endl;
}
}
//cout << pq.size() << endl;BUG:测试为0
}
return ret;
}
int main()
{
cin >> n;
for (int i = ; i <= n; i++)
{
for (int j = ; j <= n; j++)
{
cin >> cost[i][j];
if (i == j)
{
cost[i][j] = INF;
}
}
}
cout << solve() << endl;
return ;
} /*测试
5
100000 5 61 34 12
57 100000 43 20 7
39 42 100000 8 21
6 50 42 100000 8
41 26 10 35 100000
36
请按任意键继续. . .
*/

基于分支限界法的旅行商问题(TSP)一的更多相关文章

  1. 基于分支限界法的旅行商问题(TSP)二

    和上篇一样,考前写写伪代码,考完了补上具体的解释和代码. 状态{矩阵,结果集,下界} 全局结果集列表,全局上界初始为Infinite 建立一个heap,存储状态,出堆规则为拥有最小的下界. 利用red ...

  2. 【智能算法】用模拟退火(SA, Simulated Annealing)算法解决旅行商问题 (TSP, Traveling Salesman Problem)

    喜欢的话可以扫码关注我们的公众号哦,更多精彩尽在微信公众号[程序猿声] 文章声明 此文章部分资料和代码整合自网上,来源太多已经无法查明出处,如侵犯您的权利,请联系我删除. 01 什么是旅行商问题(TS ...

  3. 基于GA遗传算法的TSP旅行商问题求解

    import random import math import matplotlib.pyplot as plt import city class no: #该类表示每个点的坐标 def __in ...

  4. 遗传算法解决旅行商问题(TSP)

    这次的文章是以一份报告的形式贴上来,代码只是简单实现,难免有漏洞,比如循环输入的控制条件,说是要求输入1,只要输入非0就行.希望会帮到以后的同学(*^-^*) 一.问题描述 旅行商问题(Traveli ...

  5. 基于贪心算法求解TSP问题(JAVA)

    概述 前段时间在搞贪心算法,为了举例,故拿TSP来开刀,写了段求解算法代码以便有需之人,注意代码考虑可读性从最容易理解角度写,没有优化,有需要可以自行优化! 详细 代码下载:http://www.de ...

  6. 基于爬山算法求解TSP问题(JAVA)

    一.TSP问题 TSP问题(Travelling Salesman Problem)即旅行商问题,又译为旅行推销员问题.货郎担问题,是数学领域中著名问题之一.假设有一个旅行商人要拜访n个城市,他必须选 ...

  7. 07_旅行商问题(TSP问题,货郎担问题,经典NPC难题)

    问题来源:刘汝佳<算法竞赛入门经典--训练指南> P61 问题9: 问题描述:有n(n<=15)个城市,两两之间均有道路直接相连,给出每两个城市i和j之间的道路长度L[i][j],求 ...

  8. 禁忌搜索算法TSA 旅行商问题TSP python

    import math import random import numpy as np import matplotlib.pyplot as plt from mpl_toolkits.mplot ...

  9. 三进制状态压缩DP(旅行商问题TSP)HDU3001

    http://acm.hdu.edu.cn/showproblem.php?pid=3001 Travelling Time Limit: 6000/3000 MS (Java/Others)     ...

随机推荐

  1. 06_Android中ArrayAdapter的使用

     1 目标界面 2 编写AndroidManifest.xml文件 <?xml version="1.0" encoding="utf-8"?> ...

  2. 【一天一道LeetCode】#69. Sqrt(x)

    一天一道LeetCode 本系列文章已全部上传至我的github,地址:ZeeCoder's Github 欢迎大家关注我的新浪微博,我的新浪微博 欢迎转载,转载请注明出处 (一)题目 Impleme ...

  3. Guava 教程(3):Java 的函数式编程,通过 Google Collections 过滤和调用

    原文出处: oschina 在本系列博客的第一.二部分,我介绍了非常优秀的Google Collections和Guava包.本篇博客中我们来看看如何使用Google Collections来做到过滤 ...

  4. 2013暑假总结-SB学习

    经过暑假的学习,使英语原本基础不好的我找到了英语学习的感觉.方向.信心,暑假的这种团队学习英语的感觉才刚刚开始,即将开学了,我们并将保持着这学习的劲头坚持努力的做下去. 暑假35天英语的全职学习,对于 ...

  5. MySQL学习笔记_8_SQL语言基础复习

    SQL语言基础复习 一.概述 SQL语句注释方式 1)以"#"开头直到行尾的所有内容都是注释 2)以"--"(--后还有一个空格)开头直到行尾的所有内容都是注释 ...

  6. Android和iOS中Cocos2D日志为什么会出现skip frames

    在你运行app在Android或iOS设备或iOS模拟器中时,日志里往往会出现一行: I/Choreographer(28956): Skipped 159 frames! The applicati ...

  7. Unity3D学习笔记(四)Unity的网络基础(C#)

    一 网络下载可以使用WWW类下载资源用法:以下载图片为例WWW date = new WWW("<url>");yield return date;texture = ...

  8. FFMPEG结构体分析:AVCodecContext

    注:写了一系列的结构体的分析的文章,在这里列一个列表: FFMPEG结构体分析:AVFrame FFMPEG结构体分析:AVFormatContext FFMPEG结构体分析:AVCodecConte ...

  9. 一张图看懂AR至GL数据流

  10. 【Android 应用开发】AndroidUI设计之 布局管理器 - 详细解析布局实现

    写完博客的总结 : 以前没有弄清楚的概念清晰化 父容器与本容器属性 : android_layout...属性是本容器的属性, 定义在这个布局管理器的LayoutParams内部类中, 每个布局管理器 ...