基于分支限界法的旅行商问题(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) ...
随机推荐
- Android反编译获取源码-android学习之旅(70)
上一讲我们介绍了如何获取资源文件,这一节讲解如何获取源码,其实获取源码真的很简单 首先还是要有工具,Dex2jar,这个工具用于将apk解压之后的dex文件转化为jar文件还有jd-gui的这个工具能 ...
- android TabLayout实现京东详情效果
Google在2015的IO大会上,给我们带来了更加详细的Material Design设计规范,同时,也给我们带来了全新的Android Design Support Library,在这个supp ...
- Cocos2D遍历场景图(Scene Graph)
另一个Cocos2D有用的调试特性是打印出递归的打印出节点的孩子们. 你可以添加以下一行到MainScene或GameScene的didLoadFromCCB的方法中: [self.scene wal ...
- 【一天一道LeetCode】#19. Remove Nth Node From End of List
一天一道LeetCode系列 (一)题目 Given a linked list, remove the nth node from the end of list and return its he ...
- Leetcode_145_Binary Tree Postorder Traversal
本文是在学习中的总结,欢迎转载但请注明出处:http://blog.csdn.net/pistolove/article/details/42876769 Given a binary tree, r ...
- 写的还不错的专题,android性能优化
http://www.trinea.cn/android/android-performance-demo/
- STM32F429学习笔记(一)触屏工程Keil建立
由于原来的STM32F103ZET6的flash坏掉了,所以又买了一块STM32F429DISCOVERY,这块板子非常不错,基于Cortex-M4内核,自带一块2.4寸TFT触屏,主频为180M,且 ...
- saiku应用的调试
ubuntu下解压saiku包后使用: 运行.sh命令(.bat是windows命令).运行时注意权限.可以先chmod a+x *.sh 提示,catali?.sh出错. 这是tomcat的一个文件 ...
- adb shell后出现error解决方案
解决办法: 解决办法: 1.adb kill-server 2.adb start-server 3.adb remount 4.adb shell 一般情况下都可以在此启动adb相关
- Android ROM开发(三)——精简官方ROM并且内置ROOT权限,开启Romer之路
Android ROM开发(三)--精简官方ROM并且内置ROOT权限,开启Romer之路 相信ROM的相关信息大家通过前几篇的学习都是有所了解了,这里就不在一一提示了,这里我们下载一个官方包,我们还 ...