2016华为挑战赛_寻路_赛题分析

题意:在有向图中,给定起点和终点,求经过所有必经点集合的最短路径。(图中所有的点最多只能经过一次)

限制:点数 <= 600, 必经点数 <= 50, 每个结点的最大出度为8

这是一个NP问题,是哈密顿回路和TSP问题的变种。

分析:对于这个问题最直接的想法就是采用深度优先暴力搜索,暴力搜索算法的时间复杂度太大(2^n),只能解决30点之内的初级用例。

而对于点数较多的测试用例,尝试过下面几种方法,虽然最后的实现效果不是很理想,官方的case 14,15没有跑出来,因为设计的算法都是基于贪心的,所以就掉沟里了=_=


1. 插点法(基于floyd算法)

    idea:利用floyd算法求出起点到终点的最短路,然后在最短路中依次加入必经点集合。

    step1: 利用floyd算法求出所有节点之间的最短路,并利用path[][]数组记录下最短路径。使用passPoint[] 数组记录已经经过的必经点(按起点到终点依次经过的顺序排列)。

    step2: 插入一个必经点point。在已有的起点到终点的最短路上,选择point需要插入到路径中哪两个相邻的必经点之间,利用最小距离增量来选择插入位置。对于相邻点passPoint[i]与passPoint[i+1], 利用floyd算出的最短路得到 bias = distance(passPoint[i], point) + distance(point,passPoint[i+1]) - distance(passPoint[i], passPoint[i+1]),需要验证distance(passPoint[i], point)与distance(point,passPoint[i+1])中经过的结点是否已经被访问,如果存在被访问过的结点,则舍弃这个插入位置,遍历后求得bias最小的位置,进行插入。如果对于每一个插入的位置,都存在路径distance(passPoint[i], point)与distance(point,passPoint[i+1])中有访问过的结点,则选择冲突结点最小的那个位置,利用深度优先搜索求passPoint[i] 经过点point到达passPoint[i+1]的最短路径。重复step2,直到必经点集插入完全。

 备注:在插入一个必经结点时,引入的新路径中可能包含必经点,此时应该把这些必经结点按顺序加入到passPoint[]数组中。


2. 依次连接路径边(基于dijkstra算法)

    idea: 依次选择必经点中出度或入度最小的点,连接此点与其最近的必经点。

    改进dijkstra算法来记录必经点point的直接后继必经点序列(直接后继的意思是point能够在不经过其他必经点的情况下到达此必经点),同时也记录必经点point的直接前驱必经点序列。选择min(直接后继数量, 出度,直接前驱数量,入度)最小的点,利用dijkstra算法连接此点与其最近的必经节点。依次选择连接路径,直到完成起点到终点,经过所有必经点的路径。

   备注:此算法适用于边比较少的有向图,实现时为了避免起点到终点的路径未加入所有必经节点,而引入了禁忌边tabu[][],来标志两个点之间可否连接。


3. 基于条件点的深度优先暴力搜索(基于dijkstra算法)

   idea: 改进dijkstra算法来记录必经点point的直接后继必经点序列(直接后继的意思是point能够在不经过其他必经点的情况下到达此必经点)。从起点开始暴力搜索其直接后继必经点,每搜索到一个点,利用dijkstra求两个必经点之间最短距离,在搜索过程中采用一些方法进行剪枝,例如剩余必经点与终点的可达性。

         备注:引入markPoint[]数组,来记录每个点隶属的必经点。隶属的标准如下:

         ①如果一个必经点的出度或入度为1,那么这个点的直接前驱或直接后继结点就隶属于此结点。

         ②如果一个必经点的直接后继必经点或直接前驱必经点只有一个,那么这个必经点到直接后继点的最短路上的所有点都隶属于这两个结点。

         可以利用点的隶属属性,避免dijkstra寻路时占用属于其他必经点的重要结点。总的来说,这种方法如果没有采用有效的剪枝方式,很容易超时。

 总结:对以上3种方法进行编码实现,得到第一种方法的效果是最好的,尽管在很多情况下找到的都不是最优解。虽然最后没能进入决赛,不过在这个过程中学到了很多图论有关的算法,也算是从小白到大白的一小步吧。


    

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值