华为CodeCraft2016比赛的个人的算法优化

来源:互联网 发布:吴承恩与西游记 知乎 编辑:程序博客网 时间:2024/05/18 01:11

参加了2016华为CodeCraft比赛受到刺激很大, 感觉被那些牛人秒成渣渣. 要更努力啊! 

我已经"光荣牺牲"了, 所以也就没什么心理负担了.

题目如下:

给定一个带权重的有向图G=(V,E),V为顶点集,E为有向边集,每一条有向边均有一个权重。对于给定的顶点s、t,以及V的子集V',寻找从s到t的不成环有向路径P,使得P经过V'中所有的顶点(对经过V'中节点的顺序不做要求)。
若不存在这样的有向路径P,则输出无解,程序运行时间越短,则视为结果越优;若存在这样的有向路径P,则输出所得到的路径,路径的权重越小,则视为结果越优,在输出路径权重一样的前提下,程序运行时间越短,则视为结果越优。

题目跟旅行商问题很相似, 不同点在于一是起点和终点不同; 二是不用经过所有点, 但是必须要经过指定的点集.

我把我的想法分享一下, 跟大家做一些探讨, 欢迎大家指正.


首先是路径用什么结构来表示, 我的决定是用HashMap, 每个点保存下一个点的id. 第一个原因是这种数据结构可以很轻松的在某个点之前或者之后插入一个点, 大家可能会说LinkedList也行,但是用LinkedList你要首先定位你要查找的点吧,这就是O(n)的时间复杂度了,而HashMap不需要,知道这个点的id就行;第二个原因是哈希表能用O(1)的时间复杂度判断某个点是否在当前路径中.

其次是一些简单的优化,起点的入度可以直接删除到0,终点的出度直接删除到0, 其他点如果存在出度或者入度为0的点,递归删除.在这里还可以进行一些判断,如果有起点或者v'中的点的出度或者入度为0的话,直接判断出没有可行路径.


下面开始谈算法吧, 我的想法大致经历了三个过程:

第一个过程:

最开始完全没什么思路,试了试dfs暴力搜索,然而在v的顶点数大于50之后程序就"不理我了"/(ㄒoㄒ)/~~


第二个过程:

然后在赛事QQ群里听人讨论, 有人用遗传算法和蚁群算法得出结果, 然后自己也试着用遗传算法解题. 遗传算法的基本步骤如下: 1. 生成一定数量的初始解, 将其记为当前解, 这些解集记为第一代; 2. 对当前代, 通过交叉/变异两种操作生成一定数量的新解; 3. 在当前代和新解中找出最优的一定数量的解, 其他解的淘汰, 这些解是新的一代; 4. 重复第2和第3两个步骤, 直到指定代数.

先说说我对遗传算法的看法, 我自己对遗传算法是很没底的, 因为它不能预测程序的时间复杂度, 其中的人为因素对其影响很大, 所以我如果能用传统的有确定时间空间复杂度的算法的话, 就不会去用遗传算法. 但是听说遗传算法在最近很火的人工智能领域作用很大, 所以更加觉得自己菜了-_-",完全看不出遗传算法的优势所在. 

针对我们的问题, 要解决的问题如下:

1.如何在当前解和新解中找出最优解, 评判标准如何界定.

2. 如何生成初始解;

3.交叉/变异的概率如何确定,怎样进行交叉变异;

对这3个问题, 我的解决方法如下:

1. 评价标准我是这样考虑的, 对一条路径来说, 它包含的v'中的点越多越好,权重和越小越好,不存在的路径越少越好,因此评价是这样的, 路径好坏=(v'中的点的个数)/(权重*不存在的路径条数);

2. 用洗牌算法对所有的点(除起点和终点)随机排列生成随机序列,多生成几个,这就是初始种群(必然存在很多不存在的路径,就是两点间并不相连);

3. 交叉/变异的概率需要多试验, 取[0, 1]的值. 先说交叉,我的方法是对两条父代路径p1, p2, 从p1开始进行遍历, 如果当前需要交叉,那么从当前点开始遍历p2, 就这样交叉遍历. 不可避免的会出现已经访问过的点, 这时候看另外一条父代路径是否能接着遍历, 如果可以就从当前点开始遍历另外一条父代路径, 否则结束. 再说突变, 这个简单, 如果当前需要变异, 随机在所有的点中取个点出来, 如果当前路径已经存在这个新的点,那么放弃变异, 否则将当前这个点改变为新的点.

我试着用遗传算法解决问题, 可能是我参数不好, 结果比暴力搜索还不如...


第三个过程:

我的想法是先用Dijkstra算法找出v中每个点到其他可以直接到达(直接到达的意思是不经过v'中的其他点)的v'中的点或者是终点, 然后同样利用dfs,只不过这次是从起点开始直接找距它最近的v'中的下一个点, 如果在走到这个最近的v'中的点的路径中存在已经被访问过的点,那么视这个冲突点的前一个点为新的起点,再找距离它最近的v'中的点,就这样直到不能走下去为止(当前点的下一个点就是冲突点或者碰到终点但是v'中还有点没有经过).

当然了,为了叙述明白省略了一些内容,比如说用Dijkstra算法找最近点是用延迟计算的,就是需要的时候再计算,如果之前已经计算出结果了就不用再计算了.


应该说第三次得到了比较好的结果, 然而依然是被大神秒成渣渣的节奏! 深深感觉到了这个世界的恶意!!

代码地址:

https://github.com/hojovi/Algorithm/tree/master/huaweicodecraft2016

暴力的dfs大概能拿不到20分,

dfs加上Dijkstra算法能达到60多分,

GA我没勇气提交...

1 0
原创粉丝点击