基于分支限界法的旅行商问题(TSP)一
旅行推销员问题(英语:Travelling salesman problem, TSP)是这样一个问题:给定一系列城市和每对城市之间的距离,求解访问每一座城市一次并回到起始城市的最短回路。它是组合优化中的一个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)一的更多相关文章
- 基于分支限界法的旅行商问题(TSP)二
和上篇一样,考前写写伪代码,考完了补上具体的解释和代码. 状态{矩阵,结果集,下界} 全局结果集列表,全局上界初始为Infinite 建立一个heap,存储状态,出堆规则为拥有最小的下界. 利用red ...
- 【智能算法】用模拟退火(SA, Simulated Annealing)算法解决旅行商问题 (TSP, Traveling Salesman Problem)
喜欢的话可以扫码关注我们的公众号哦,更多精彩尽在微信公众号[程序猿声] 文章声明 此文章部分资料和代码整合自网上,来源太多已经无法查明出处,如侵犯您的权利,请联系我删除. 01 什么是旅行商问题(TS ...
- 基于GA遗传算法的TSP旅行商问题求解
import random import math import matplotlib.pyplot as plt import city class no: #该类表示每个点的坐标 def __in ...
- 遗传算法解决旅行商问题(TSP)
这次的文章是以一份报告的形式贴上来,代码只是简单实现,难免有漏洞,比如循环输入的控制条件,说是要求输入1,只要输入非0就行.希望会帮到以后的同学(*^-^*) 一.问题描述 旅行商问题(Traveli ...
- 基于贪心算法求解TSP问题(JAVA)
概述 前段时间在搞贪心算法,为了举例,故拿TSP来开刀,写了段求解算法代码以便有需之人,注意代码考虑可读性从最容易理解角度写,没有优化,有需要可以自行优化! 详细 代码下载:http://www.de ...
- 基于爬山算法求解TSP问题(JAVA)
一.TSP问题 TSP问题(Travelling Salesman Problem)即旅行商问题,又译为旅行推销员问题.货郎担问题,是数学领域中著名问题之一.假设有一个旅行商人要拜访n个城市,他必须选 ...
- 07_旅行商问题(TSP问题,货郎担问题,经典NPC难题)
问题来源:刘汝佳<算法竞赛入门经典--训练指南> P61 问题9: 问题描述:有n(n<=15)个城市,两两之间均有道路直接相连,给出每两个城市i和j之间的道路长度L[i][j],求 ...
- 禁忌搜索算法TSA 旅行商问题TSP python
import math import random import numpy as np import matplotlib.pyplot as plt from mpl_toolkits.mplot ...
- 三进制状态压缩DP(旅行商问题TSP)HDU3001
http://acm.hdu.edu.cn/showproblem.php?pid=3001 Travelling Time Limit: 6000/3000 MS (Java/Others) ...
随机推荐
- 发布Ext JS 5.1 beta版本
原文:Announcing Ext JS 5.1 Beta 概述 我们很高兴的宣布,Ext JS 5.1 beta发布了.自从Ext JS 5.0.1,我们一直在努力添加一些令人兴奋的和一些在Senc ...
- 内连接、左外连接、右外连接、全外连接、交叉连接(CROSS JOIN)-----小知识解决大数据攻略
早就听说了内连接与外连接,以前视图中使用过.这次自考也学习了,只是简单理解,现在深入探究学习(由于上篇博客的出现)与实践: 概念 关键字: 左右连接 数据表的连接有: 1.内连接(自然连接): 只有两 ...
- MySQL学习笔记_5_SQL语言的设计与编写(上)
SQL语言的设计与编写(上) 一.SQL语句分类 数据定义语言(DDL): 用于定义和管理数据对象,包括数据库.数据表.视图.索引等.例如:CREATE.DROP.ALTER等语句. 数据操作语言(D ...
- 程序员的软实力武器-star法则
hhh 程序员的表达能力一直被诟病,尤其面试讲述自己的项目的时候 下面的star原则能够帮助你: 所谓STAR原则,即Situation(情景).Task(任务).Action(行动)和Result( ...
- H5学习之旅-H5列表(8)
列表的基本语法 ol:有序列表 ul:无序列表 li:列表项 dl:列表 dt:列表项 dd:列表描述 常用列表 1.无序列表:使用标签 ul,li 属性:disc(默认实心圆) circle (空心 ...
- sublimeText3使用记录
sublime确实是神器,最近学习了一下,做个记录 1.下载 http://www.sublimetext.com/3 选择对应的版本安装即可(我的是win10 64位机) 2.个人配置 2.1.默认 ...
- 查看Linux系统的平均负载
1.Linux系统的平均负载的概念 有时候我们会觉得系统响应很慢,但是又找不到原因,这时就要查看平均负载了,看它是否有大量的进程在排队等待.特定时间间隔内运行队列中的平均进程数可以反映系统的繁忙程度, ...
- Memcached的配置,SSH项目中的整合(com.whalin),Memcached工具类,Memcached的代码调用
1 修改pom.xml,添加依赖文件: <dependency> <groupId>com.whalin</groupId> <artifactId&g ...
- platform_driver_probe与platform_driver_register的区别
Platform Device and Drivers 从<linux/platform_device.h>我们可以了解Platform bus上面的驱动模型接口:platform_de ...
- Android Studio JNI javah遇到的问题
好久没写博客了.持之以恒的勋章也被收回了.以后要好好坚持.. 最近在学习jni,但是遇到了一点麻烦的问题.好在终于解决了,便记下来解决一下. 其他入门的jni文章有很多,这里便不在累赘,直接上我遇到的 ...