<路径算法>哈密顿路径变种问题(2016华为软件精英挑战赛初赛)
原创博客,转载请联系博主!
前言:几天前华为的这个软件精英(算法外包)挑战赛初赛刚刚落幕,其实这次是我第二次参加,只不过去年只入围到了64强(32强是复赛线),最后搞到了一个华为的一顶帽子(感谢交大lady快递寄过来!),今年小较了一把真,幸运地闯进了排行榜。(第17位的就是我们Team噢!耶鲁顾神很给力!)
所以呢,回到正题首先来看一下初赛赛题吧!
初赛赛题要求
已知有向图G的拓扑(结点V,边E)和V的一个子图V’,在G内求一条从start结点到end结点的路径,要求经过V’的所有结点并且整个路径要求每个结点每条边只经过一次,在规定的时间内求得的路径的权重越小,则算法得分越高。(start和end结点不在V’中)
当然华为官方提供了专用的sdk(其实用起来很麻烦的好嘛?!)
为什么说这个问题是哈密顿路径问题的变种呢?首先来看一下哈密顿路径问题的定义吧:( 百度百科 )( 维基百科 )
这个赛题在的一个子问题是在V’内求一条哈密顿路径,这个问题本身就是一个np完全的问题 ,直接暴力求解会导致计算时间指数增长,当然利用状态压缩的动态规划可以让哈密顿路径问题在时间复杂度上得到一定的优化。但是无论怎样在这道题中,想要在10秒内稳定地求得要求的路径是随着图的规模增大几乎是不可能的。
所以我们解题的思路经历了如下所示的变化:
深搜/广搜剪枝 -> 遗传算法+深搜 -> 线性规划
解题心路历程
第一阶段 - 深搜/广搜剪枝
其实暴力搜索+剪枝的解法在图的规模比较小的情况下,性能表现甚至会超过其他复杂的算法:
递归的的思路非常简单:
recTraversalGraph:
for every Vertex ’v’ unvisited
mark ’v’ as visited
recTraversalGraph v
unmark 'v' as unvisited
end for
当然直接这么去遍历图的代价是巨大的,所以我们加了一些剪枝的条件:
1. 当前遍历到的点为终点的时候,判断路径是否符合题意,是则记录,否则剪枝。
2. 当前遍历的深度时的cost,已经大于外部记录的最小cost时,剪枝。
3. 当前遍历到的某个边的cost已经大于外部记录的最小cost时,在临接链表删除这个边。
除了剪枝之外,我们还需要简单地压缩图:
1. 将出度仅为1的点和相连接的点merge为同一个点,这个应该很容易理解,暂不赘述。
2. 将出发点和到达点相同的边去除重复,只保留cost最小的边。
当然这么做的提交后的结果果然是令人失望的,只解出了初级的5个测试用例,貌似这些初级用例的点规模不超过100,不过也是在预料之中的事情。
第二阶段 - 遗传算法 + 深搜
到这里我们的算法参考了几篇关于TSP问题的遗传算法求解的论文,在我们决定使用遗传算法的时候,官方提供的比赛环境还是双核的,所以我就果断选择了拥有天生并行性的遗传算法来初步解决,我们的问题,然后再使用深搜去寻找可行/最优解。
针对这个问题我们的遗传算法的解决思路是这样的:
在开始之前我们需要一个SPFA算法,参数可以指定一些图中剔除掉的点,利用Java特性重载Object.clone()即可做到。
for every Vertex ‘v’ in V'
计算 ‘v’ 的单源最短路径
end for
到这里我们得到了一个|V| * |V'|的最短路径cost表和一张|V| * |V'|的路线记录表(路径经过节点顺序,实质上是个生成树)。
然后开始进行遗传算法,这里有三种元操作,详细的介绍请参考上面提供的论文链接。
1. 交配操作,针对TSP问题,我们的交配操作采用的不是通用的k-opt,而是一种叫做‘顺序交叉’的方法。大致就是对于如下所示的AB两个路径序列:
|--- A1 ---|--- A2 ---|--- A3 ---|
|--- B1 ---|--- B2 ---|--- B3 ---|
取随机的两个切割点得到A和B的3*2个子序列,交换A2和B2并且做一些处理,基于随机数解决重复,得到两个孩子序列。
2. 点交换操作
顾名思义,即由随机数交换单个序列上的若干个点后,突变得到的子序列。
3. 段交换操作
和上述操作相似,即由随机数交换单个序列上的若干个段后,突变得到的子序列。
进入遗传算法迭代循环:
while 没有达到大致收敛
RoundRobin法摇随机数
if 符合交配概率: 进行交配操作并淘汰劣者
elsif 符合点突变概率: 进行点突变操作并淘汰劣者
elsif 符合段突变概率: 进行段突变操作并淘汰劣者
else do nothing
end while
值得一提的是,我们利用Java的特性在利用并行性之余,加入了util.concurrent.Exchanger来让两个并行的群组之间一定迭代次数后交换最优秀的个体。
在遗传算法结束之后,我们可以得到若个干个较优秀的候选个体,之后我们需要使用深搜来从这些已知的个体中找出可行解。因为上面遗传算法的评分依据是之前求得的最短路径cost表,我们需要再根据与之对应的最短路径记录表寻找出最短路径之中重叠的边,从图中剔除然后利用带剔除边的SPFA重新计算最短路径。这样一来也就顺便解决了成环的问题。再加上候选个体的数目可以由我们手动控制,所以整个深搜的计算时间是受控制的。初赛赛题中最大规模的点的规模为600个点,遗传算法完成收敛大概需要6~7秒的计算时间,尽管到遗传算法结束,算法的表现依然良好,但是问题出现在了后面。
由于赛题提供的测试用例图都非常稀疏,剔除掉某个边想求出可行解的想法简直Naive,果不其然,我们的算法尽管时间控制得还好,但是将大部分有解的case判定为了无解,尤其是对于唯一可行解就是最优解的case来讲,我们的算法简直不堪一击。在痛下决心之后,我们决定使用数学建模+线性规划来解决这个问题!
第三阶段 - 数学建模+线性规划
我们的规划模型经历了一系列的变化,优化过程其实没有太多参考意义可言,最后的数学模型是这样的:
1. 定义
E1...En为从索引为1...n的边,C1...Cn为边E1...En对应的权重。
系数矩阵为01矩阵,系数矩阵0代表不选择这条边,1代表选择这条边,描述为X1...Xn。
2. 约束函数
对于V内的每个点满足入边系数之和小于等于1: 即每个点最多只能有1个入边。
对于V内的每个点满足出边系数之和小于等于1: 即每个点最多只能由1个出边。
对于V内的每个点满足出边系数之和减去入边系数之和等于0: 即每个点的出边个数和入边个数相等。
对于V‘内的每个点满足出边系数之和等于1: 即必有出边。
对于V‘内的每个点满足入边系数之和等于1: 即必有入边。
无环约束: 1 <= Ui - Uj + Xij*|V| <= |V| - 1
3. 目标函数
MINIMIZE ( C1X1 + C2X2 + ... + Cn-1Xn-1 + CnXn )
一开始我们选择了 GLPK 作为开源库帮助我们求解,但是解不出官方提供的最难的两个case,导致得分一直低迷。后来使用 SCIP-soplex 这个开源库,同样的模型来解决这个问题,再加上SCIP提供了一些非常优秀的启发式,导致我们的得分一路上升。
最后思路锁定下来是这样的:
对于规模小的case,使用深搜+剪枝。
对于规模中的case,使用广搜+剪枝。
对于规模大的case,使用随机化的启发式搜索。
当然最后还有一些优化是语言层面的,比如数组压缩,动态分配之类等等,这篇文章没有聊太多关于实现的细节,有人对具体代码和实现细节感兴趣欢迎留言或者联系我。
<路径算法>哈密顿路径变种问题(2016华为软件精英挑战赛初赛)的更多相关文章
- 2021华为软件精英挑战赛(C/C++实现)-苦行僧的实现过程
下面给出2021华为软件精英挑战赛参与的整个过程,虽然成绩不是很好,但是也是花了一些时间的,希望后面多多学习,多多进步. 代码已经上传到了Github上:https://github.com/myFr ...
- 如何使用华为软件开发云快速部署PHP网站
华为软件开发云这个工具,从去年推出我就一直在关注,毕竟是华为最新的一款软件开发工具,最近我一直在使用华为软件开发云进行开发项目管理,它有在线编译和构建.云端在线代码检查等功能,编译省去了很多物理机器的 ...
- 华为软件开发云对比Jenkins-JavaWeb项目持续部署方式
一.前言:Jenkins介绍 Jenkins是一个开源软件项目,是基于Java开发的一种持续集成工具,用于监控持续重复的工作,旨在提供一个开放易用的软件平台,使软件的持续集成和持续部署变成可能. 本文 ...
- 纯干货!华为软件开发云编译构建之Maven
一.Maven介绍 Maven是一个项目管理和整合的工具.Maven为开发者提供了一套完整的构建生命周期框架.开发团队基本不用花多少时间就能自动完成工程的基础构建配置,因为Maven使用了一个标准的目 ...
- Eclipse安装Git插件以及通过Git导入华为软件开发云项目
--内容提交-- 1. Eclipse安装Git插件 2. 在Eclipse中导入华为软件开发云项目, 以及常用Git操作 一. Eclipse安装Git插件 现在从eclipse官网下载 ...
- GitHub转华为软件开发云详细教程
一.复制GitHub的代码库地址 首先,打开Github网页,找到要迁移的代码仓库地址,如下: 点击Clone or Download,出现以下界面 点击Copy toclipboard(复制到粘贴板 ...
- 如何将Android Studio与华为软件开发云代码仓库无缝对接(二)
上篇文章:如何将Android Studio与华为软件开发云代码仓库无缝对接(一) 上一章讲了,如何用Android Studio以软件开发云代码仓库为基础,新建一个项目.接下来,这一章继续讲建好项目 ...
- 华为软件开发云CloudIDE功能简测
华为软件开发云CloudIDE是面向软件开发者的云端开发环境,支持在线编写代码,智能提示,代码提交,代码片段智能搜索等功能. CloudIDE实现了大部分IDE的功能,同时克服了全能IDE的缺点. 一 ...
- 看完给跪了:技术大牛总结的Github与华为软件开发云完整对比
华为软件开发云配置管理 服务和Github是国内外比较有代表性的代码托管平台,它们以git作为版本管理工具,使项目中身处各地的人员可以协同工作,主要操作涉及仓库.分支.提交.pull request等 ...
随机推荐
- 像装软件一样装系统 Win8下怎么装Win7
像装软件一样装系统 Win8下怎么装Win7 首先,你需要一个Windows7的ISO镜像文件,非ghost版本 一般选中ISO文件,点反键在弹出菜单中以“装载”或“window资源管理器”方式打开 ...
- css设置背景图片,ie显示不了
本来是想给导航栏<div class="nav"></div>添加背景图片的,设置css样式如下: .nav{background:url("ht ...
- .NET中值得体验的精妙设计
转自: http://developer.51cto.com/art/201104/255455_all.htm .NET 是 Microsoft XML Web services 平台.MEF是.N ...
- FLEX 特效
一.简介: flex特效是ria应用程序的rich的重要组成部分. EffectManager类管理所有的特效实例以避免不必要的定时器和方法调用造成的内内存使用过大.一个效果由两部分组成:一是效果的E ...
- 黄聪:mysql 存在该记录则更新,不存在则插入记录的sql
一条mysql教程 存在该记录则更新,不存在则插入记录的sql , ‘yourname') ON DUPLICATE KEY UPDATE auto_name='yourname' ON DUPLIC ...
- ARM NEON 编程系列2 - 基本指令集
ARM NEON 编程系列2 - 基本指令集 前言 本系列博文用于介绍ARM CPU下NEON指令优化. 博文github地址:github 相关代码github地址:github NEON指令集 主 ...
- 0811 css样式表基本
CSS(Cascading Style Sheets,层叠样式表),作用是美化HTML网页. /*注释*/ 注释语法 1. 样式表分类 ①内联样式表 和html联合显示,控制精确,但是可重用性差 ...
- [复变函数]第15堂课 4.3 解析函数的 Taylor 展式
1. Taylor 定理: 设 $f(z)$ 在 $K:|z-a|<R$ 内解析, 则 $$\bee\label{15:taylor} f(z)=\sum_{n=0}^\infty c_n(z ...
- dos查看端口
C:\Documents and Settings\Administrator>netstat -an | findstr "22" TCP 192.168.16.2 ...
- .Net中Math.Round与四舍五入
有不少人误将Math.Round函数当作四舍五入函数在处理, 结果往往不正确, 实际上Math.Round采用的是国际通行的是 Banker 舍入法. Banker's rounding(银行家舍入) ...