【旅行商问题】旅行商问题(TravelingSalesmanProblem,TSP)是一个经典的组合优化问题。经典的TSP可以描述为:一个商品推销员要去若干个城市推销商品,该推销员从一个城市出发,需要经过所有城市后,回到出发地。应如何选择行进路线,以使总的行程最短。从图论的角度来看,该问题实质是在一个带权完全无向图中,找一个权值最小的Hamilton回路。由于该问题的可行解是所有顶点的全排列,随着顶点数的增加,会产生组合爆炸,它是一个NP完全问题。由于其在交通运输、电路板线路设计以及物流配送等领域内有着广泛的应用,国内外学者对其进行了大量的研究。早期的研究者使用精确算法求解该问题,常用的方法包括:分枝定界法、线性规划法、动态规划法等。但是,随着问题规模的增大,精确算法将变得无能为力,因此,在后来的研究中,国内外学者重点使用近似算法或启发式算法,主要有遗传算法、模拟退火法、蚁群算法、禁忌搜索算法、贪婪算法和神经网络等。【参考百度百科】。
旅行商问题:字母代表城市,数字代表城市间的路径或者费用
蛮力法求解上图的所有路径并记录最佳路径的相关信息
旅行商求解系列:
-------------------------------------------------------------------------------------------------
(1)TSP_旅行商问题- 蛮力法( 深度遍历优先算法DFS )
(2)TSP_旅行商问题- 动态规划
(3)TSP_旅行商问题- 模拟退火算法
(4)TSP_旅行商问题- 遗传算法
(5)TSP_旅行商问题- 粒子群算法
(6)TSP_旅行商问题- 神经网络
-------------------------------------------------------------------------------------------------
本文基于蛮力法(此处采用深度优先遍历,DFS)解决旅行商问题。通过遍历出所有满足条件的路径情况,并保持更新最优解,直到所有情况都遍历完,得到全局最优解。但是,使用蛮力法需要遍历的城市个数高达city_num的阶乘,当city_num=12的时候,需遍历479001600种情况,程序所需时间以小时为单位。
void CreateGraph(Graph &G){
ifstream read_in;
read_in.open("L:\\Coding\\图的常见操作\\图的常见操作\\city_10.txt");
if (!read_in.is_open())
{
cout<<"文件读取失败."<> G.vex_num;
G.arc_num = 0;
for (int i = 0;i < G.vex_num; i++)
{
read_in >> G.vexs[i];
}
G.vexs[G.vex_num] = '\0'; // char的结束符.
for (int i = 0; i < G.vex_num;i++)
{
for (int j = 0; j < G.vex_num; j++)
{
read_in >> G.arcs[i][j];
// calculate the arc_num
if (G.arcs[i][j] > 0)
{
G.arc_num++;
}
}
}
// display
cout<<"无向图创建完毕,相关信息如下:"<
void DFS(Graph G, char city_start){
int v_index = _findCityIndex(G, city_start); // 起始城市,每次调用(递归)都更新.
if (path_index == G.vex_num - 1 && G.arcs[v_index][int('A') - 65] > 0)
{
path_DFS[path_num][path_index] = city_start;
path_DFS[path_num][path_index + 1] = 'A'; // A为起始城市
lenth_DFS[path_num] = 0; // 存储最短路径
// 计算最短路径
for (int i = 0; i < G.vex_num; i++)
{
lenth_DFS[path_num] += G.arcs[(int)path_DFS[path_num][i] - 65][(int)path_DFS[path_num][i+1] - 65];
}
if (bestLength > lenth_DFS[path_num])
{
// 更新最短路径
bestLength = lenth_DFS[path_num];
}
//cout << "第【" << (path_num + 1) << "】条路径!" << endl;
DFS_fout << "第【" << (path_num + 1) << "】条路径!" << endl;
path_num++; // 下一条路径
// 初始化下一次路径与上一次相同
for (int i = 0; i < G.vex_num;i++)
{
path_DFS[path_num][i] = path_DFS[path_num-1][i];
}
return;
}
else
{
for (int i = 0; i < G.vex_num; i++)
{
if (G.arcs[v_index][i] > 0 && !is_visited[i])
{
path_DFS[path_num][path_index] = city_start;
path_index++;
is_visited[v_index] = true;
DFS(G, (char)(i + 65));
// cout<<"--- 深度遍历回溯 ---"<
1)采用邻接表表示的图深度遍历 -----》 时间复杂度为O(n+e);
2)采用邻接矩阵表示的图深度遍历 -----》 时间复杂度为O(n^2);
bool is_visited[max_vexNum]; // 存储当前城市是否已被访问
char path_DFS[MAX_PATH_LENGTH][max_vexNum]; // 存储所有路径
double lenth_DFS[MAX_PATH_LENGTH]; // 存储所有路径对应的长度
10
A B C D E F G H I J
0 2538.94 2873.8 2575.27 2318.1 2158.71 2216.58 3174.04 3371.13 3540.24
2538.94 0 1073.54 111.288 266.835 395.032 410.118 637.942 853.554 1055
2873.8 1073.54 0 964.495 988.636 1094.32 1382.73 1240.15 1460.25 1687
2575.27 111.288 964.495 0 262.053 416.707 503.563 624.725 854.916 1068.42
2318.1 266.835 988.636 262.053 0 163.355 395.14 885 1110.86 1318.19
2158.71 395.032 1094.32 416.707 163.355 0 338.634 1030.34 1248.58 1447.69
2216.58 410.118 1382.73 503.563 395.14 338.634 0 984.068 1160.26 1323.7
3174.04 637.942 1240.15 624.725 885 1030.34 984.068 0 243.417 473.768
3371.13 853.554 1460.25 854.916 1110.86 1248.58 1160.26 243.417 0 232.112
3540.24 1055 1687 1068.42 1318.19 1447.69 1323.7 473.768 232.112 0
#ifndef _DFS_H_
#define _DFS_H_
/* 1. 图 - 邻接矩阵表示法 */
/* ---------------------------------------------------------------- */
/* 较完善的数据结构
#define VRType int
#define InfoType int
#define VertexType char
#define max_n 20
typedef enum{DG, DN, AG, AN} GraphKind;
// 弧结点与矩阵的类型
typedef struct {
VRType adj; //VRType为弧的类型。图--0,1;网--权值
InfoType *Info; //与弧相关的信息的指针,可省略
}ArcCell, AdjMatrix[max_n][max_n];
// 图的类型
typedef struct{
VertexType vexs[max_n]; // 顶点向量
AdjMatrix arcs; // 邻接矩阵
int vexnum, arcnum; // 顶点数,边数
GraphKind kind; // 图类型
}MGraph;
*/
/* ---------------------------------------------------------------- */
/* 简化的数据结构 */
#define max_vexNum 26 // 最大城市个数
#define MAX_PATH_LENGTH 9999999
typedef struct{
int vex_num, arc_num; // 顶点数 边数
char vexs[max_vexNum]; // 顶点向量
double arcs[max_vexNum][max_vexNum]; // 邻接矩阵
}Graph;
void CreateGraph(Graph &G);
void DFS_Traverse(Graph G);
void DFS(Graph G, char city_start); // 深度优先遍历 - stack
void BFS(Graph G); // 广度优先遍历 - queue
bool is_visited[max_vexNum]; // 存储当前城市是否已被访问
char path_DFS[MAX_PATH_LENGTH][max_vexNum]; // 存储所有路径
double lenth_DFS[MAX_PATH_LENGTH]; // 存储所有路径对应的长度
long int path_num = 0, path_index = 0;
long double bestLength = INT_MAX + 0.0; // 最短路径初始化为无穷大
// 功能函数
void CreateGraph(Graph &G);
int _findCityIndex(Graph G, char city_start);
void DFS(Graph G, char city_start);
#endif //_BP_H_
#include
#include
#include
#include
#include
#include // 本文用于输出对齐
#include "DFS.h"
using namespace std;
ofstream DFS_fout("L:\\Coding\\图的常见操作\\图的常见操作\\city_10_out_2.txt");
int main(){
cout<<"程序开始..."<> G.vex_num;
G.arc_num = 0;
for (int i = 0;i < G.vex_num; i++)
{
read_in >> G.vexs[i];
}
G.vexs[G.vex_num] = '\0'; // char的结束符.
for (int i = 0; i < G.vex_num;i++)
{
for (int j = 0; j < G.vex_num; j++)
{
read_in >> G.arcs[i][j];
// calculate the arc_num
if (G.arcs[i][j] > 0)
{
G.arc_num++;
}
}
}
// display
cout<<"无向图创建完毕,相关信息如下:"< 0)
{
path_DFS[path_num][path_index] = city_start;
path_DFS[path_num][path_index + 1] = 'A'; // A为起始城市
lenth_DFS[path_num] = 0; // 存储最短路径
// 计算最短路径
for (int i = 0; i < G.vex_num; i++)
{
lenth_DFS[path_num] += G.arcs[(int)path_DFS[path_num][i] - 65][(int)path_DFS[path_num][i+1] - 65];
}
if (bestLength > lenth_DFS[path_num])
{
// 更新最短路径
bestLength = lenth_DFS[path_num];
}
DFS_fout << "第【" << (path_num + 1) << "】条路径!" << endl;
path_num++; // 下一条路径
// 初始化下一次路径与上一次相同
for (int i = 0; i < G.vex_num;i++)
{
path_DFS[path_num][i] = path_DFS[path_num-1][i];
}
return;
}
else
{
for (int i = 0; i < G.vex_num; i++)
{
if (G.arcs[v_index][i] > 0 && !is_visited[i])
{
path_DFS[path_num][path_index] = city_start;
path_index++;
is_visited[v_index] = true;
DFS(G, (char)(i + 65));
path_index--;
is_visited[v_index] = false;
}
}
}
}