用动态规划算法解Travelling Salesman Problem(TSP)问题

2023-11-05 23:20

本文主要是介绍用动态规划算法解Travelling Salesman Problem(TSP)问题,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

用动态规划算法解Travelling Salesman Problem(TSP)问题

  • 基础知识
  • 动态规划的求解过程
    • 动态规划方程的推导
    • 状态压缩
  • 源码:
  • 输入数据:

基础知识

  Travelling Salesman Problem (TSP) 是最基本的路线问题。它寻求的是旅行者由起点出发,通过所有给定的需求点后,再次返回起点所花费的最小路径成本,也叫旅行商问题、旅行推销员问题、担货郎问题等。
  动态规划算法(Dynamic Programming,简称DP)通常用于求解具有某种最优性质的问题,其基本思想是将待求解问题分解成若干个子问题,先求解子问题,然后由这些子问题的解再得到原问题的解。

动态规划的求解过程

  下面来验证一下此方法求解的可行性。
  设 s,s1,s2…s为满足题意的最短回路。假设从s到s1的路径已经确定,则问题转化为从s1到s的最短路径问题。而很显然,s1,s2…s一定可以构成一条最短路径,所以构成最优子结构性质,可以用动态规划求解。

动态规划方程的推导

  用 V’ 表示一个点的集合,假设从顶点 s 出发, d ( i , V’ ) 表示当前到达顶点 i,经过 V’ 集合中所有顶点一次的最小花费。

  1. .当 V’ 为仅包含起点的集合,也就是
    d ( s , { s } ) = 0 d(s,\{ s\} ) = 0 d(s,{s})=0
  2. 其他情况,则对子问题求最优解。需在 V’ 这个城市集合中,尝试每一个城市结点,并求出最优解。
    在这里插入图片描述
  3. 最后的求解方式为:
    在这里插入图片描述

其中 S 为包含所有点的集合。把公式一套,题就解了。

状态压缩

  推到动态规划方程时,我们注意到 V’ 是一个数的集合,而且解决的问题规模比较小,于是可以用一个二进制数来存储这个集合。简单来说就是——如果城市 k 在集合 V’ 中,那么存储集合的变量 i 的第 k 位就为 1,否则为 0。由于有 n 个城市,所有的状态总数我们用 M 来表示,那么很明显:M = 2^n,而 0 到 2^n -1 的所有整数则构成了 V’ 的所有状态。这样,结合位运算,动归方程的状态表示就很容易了。

源码:

#include<string>
#include<iostream>
#include<algorithm>
using namespace std;
// 定义常量
const int INF = 0x3f3f3f3f;
#define sqr(x) ((x)*(x))
// 定义变量
string file_name;
int type; // type == 1 满秩矩阵格式, type == 2 二维坐标式
int s;
int N;// 城市结点数量
int init_point;
double **dp; // 动态规划状态数组dp[i][j],i表示集合V’,j表示当前到达的城市结点
double **dis; // 两个城市结点之间的距离
double ans;
// 定义结构体
struct vertex {double x, y; // 城市结点的坐标int id; // 城市结点的idint input(FILE *fp) {return fscanf(fp, "%d %lf %lf", &id, &x, &y);}
}*node;double EUC_2D(const vertex &a, const vertex &b) {return sqrt(sqr(a.x - b.x) + sqr(a.y - b.y));
}void io() { // 数据读入printf("input file_name and data type\n");cin >> file_name >> type;FILE *fp = fopen(file_name.c_str(), "r");fscanf(fp, "%d", &N);node = new vertex[N + 5];dis = new double*[N + 5];if (type == 1) {for (int i = 0; i < N; i++) {dis[i] = new double[N];for (int j = 0; j < N; j++)fscanf(fp, "%lf", &dis[i][j]);}}else {for (int i = 0; i < N; i++)node[i].input(fp);for (int i = 0; i < N; i++) {dis[i] = new double[N];for (int j = 0; j < N; j++)dis[i][j] = EUC_2D(node[i], node[j]);// 计算城市之间的距离}}fclose(fp);return;
}void init() { // 数据初始化dp = new double*[(1 << N) + 5];for (int i = 0; i < (1 << N); i++) {dp[i] = new double[N + 5];for (int j = 0; j < N; j++)dp[i][j] = INF;} // 初始化,除了dp[1][0],其余值都为INFans = INF;return;
}double slove() {int M = (1 << N);// M就是第四部分所说的V’状态总数,1<<N表示2^N,总共有2^N种状态dp[1][0] = 0;// 假设固定出发点为0,从0出发回到0的花费为0。TSP只要求是一个环路,所以出发点可以任选for (int i = 1; i < M; i++) {// 枚举V’的所有状态for (int j = 1; j < N; j++) {// 选择下一个加入集合的城市if (i & (1 << j)) continue;// 城市已经存在于V’之中if (!(i & 1)) continue;// 出发城市固定为0号城市for (int k = 0; k < N; k++) {// 在V’这个城市集合中尝试每一个结点,并求出最优解if (i & (1 << k)) {// 确保k已经在集合之中并且是上一步转移过来的结点dp[(1 << j) | i][j] = min(dp[(1 << j) | i][j], dp[i][k] + dis[k][j]); // 转移方程} // 将j点加入到i集合中}}}for (int i = 0; i < N; i++)ans = min(dp[M - 1][i] + dis[i][0], ans);// 因为固定了出发点,所以要加上到城市0的距离。另外要从所有的完成整个环路的集合V’中选择,完成最后的转移return ans;
}int main() {io();init();string tmp = file_name + ".sol";FILE *fp = fopen(tmp.c_str(), "w");fprintf(fp, "%.2lf\n", slove());delete[] dp;delete[] node;delete[] dis;fclose(fp);return 0;
}

输入数据:

若城市数据文件如下所示:

     161   38.24   20.422   39.57   26.153   40.56   25.324   36.26   23.125   33.48   10.546   37.56   12.197   38.42   13.118   37.52   20.449   41.23   9.1010   41.17   13.0511   36.08   -5.2112   38.47   15.1313   38.15   15.3514   37.51   15.1715   35.49   14.3216   39.36   19.56

这篇关于用动态规划算法解Travelling Salesman Problem(TSP)问题的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/353106

相关文章

Android开发中gradle下载缓慢的问题级解决方法

《Android开发中gradle下载缓慢的问题级解决方法》本文介绍了解决Android开发中Gradle下载缓慢问题的几种方法,本文给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧... 目录一、网络环境优化二、Gradle版本与配置优化三、其他优化措施针对android开发中Gradle下载缓慢的问

关于Nginx跨域问题及解决方案(CORS)

《关于Nginx跨域问题及解决方案(CORS)》文章主要介绍了跨域资源共享(CORS)机制及其在现代Web开发中的重要性,通过Nginx,可以简单地解决跨域问题,适合新手学习和应用,文章详细讲解了CO... 目录一、概述二、什么是 CORS?三、常见的跨域场景四、Nginx 如何解决 CORS 问题?五、基

MySQL安装时initializing database失败的问题解决

《MySQL安装时initializingdatabase失败的问题解决》本文主要介绍了MySQL安装时initializingdatabase失败的问题解决,文中通过图文介绍的非常详细,对大家的学... 目录问题页面:解决方法:问题页面:解决方法:1.勾选红框中的选项:2.将下图红框中全部改为英

前端 CSS 动态设置样式::class、:style 等技巧(推荐)

《前端CSS动态设置样式::class、:style等技巧(推荐)》:本文主要介绍了Vue.js中动态绑定类名和内联样式的两种方法:对象语法和数组语法,通过对象语法,可以根据条件动态切换类名或样式;通过数组语法,可以同时绑定多个类名或样式,此外,还可以结合计算属性来生成复杂的类名或样式对象,详细内容请阅读本文,希望能对你有所帮助...

golang字符串匹配算法解读

《golang字符串匹配算法解读》文章介绍了字符串匹配算法的原理,特别是Knuth-Morris-Pratt(KMP)算法,该算法通过构建模式串的前缀表来减少匹配时的不必要的字符比较,从而提高效率,在... 目录简介KMP实现代码总结简介字符串匹配算法主要用于在一个较长的文本串中查找一个较短的字符串(称为

Nginx实现动态封禁IP的步骤指南

《Nginx实现动态封禁IP的步骤指南》在日常的生产环境中,网站可能会遭遇恶意请求、DDoS攻击或其他有害的访问行为,为了应对这些情况,动态封禁IP是一项十分重要的安全策略,本篇博客将介绍如何通过NG... 目录1、简述2、实现方式3、使用 fail2ban 动态封禁3.1 安装 fail2ban3.2 配

Vue3中的动态组件详解

《Vue3中的动态组件详解》本文介绍了Vue3中的动态组件,通过`component:is=动态组件名或组件对象/component`来实现根据条件动态渲染不同的组件,此外,还提到了使用`markRa... 目录vue3动态组件动态组件的基本使用第一种写法第二种写法性能优化解决方法总结Vue3动态组件动态

Nginx启动失败:端口80被占用问题的解决方案

《Nginx启动失败:端口80被占用问题的解决方案》在Linux服务器上部署Nginx时,可能会遇到Nginx启动失败的情况,尤其是错误提示bind()to0.0.0.0:80failed,这种问题通... 目录引言问题描述问题分析解决方案1. 检查占用端口 80 的进程使用 netstat 命令使用 ss

通俗易懂的Java常见限流算法具体实现

《通俗易懂的Java常见限流算法具体实现》:本文主要介绍Java常见限流算法具体实现的相关资料,包括漏桶算法、令牌桶算法、Nginx限流和Redis+Lua限流的实现原理和具体步骤,并比较了它们的... 目录一、漏桶算法1.漏桶算法的思想和原理2.具体实现二、令牌桶算法1.令牌桶算法流程:2.具体实现2.1

mybatis和mybatis-plus设置值为null不起作用问题及解决

《mybatis和mybatis-plus设置值为null不起作用问题及解决》Mybatis-Plus的FieldStrategy主要用于控制新增、更新和查询时对空值的处理策略,通过配置不同的策略类型... 目录MyBATis-plusFieldStrategy作用FieldStrategy类型每种策略的作