2017中兴捧月算法精英挑战赛-迪杰斯特拉

本文介绍了一种解决复杂蚁巢迷宫中寻找最优路径的问题。通过将约束条件简化并分段规划路径,设计了一种算法来找到满足特定条件的最短路径。

比赛提交paper、源码、可执行文件

赛题:

最强大脑中的收官蜂巢迷宫变态级挑战,相信大家都叹为观止!最强大脑收官战打响后,收视率节节攀升,就连蚁后也不时出题难为一下她的子民们。在动物世界中,称得上活地图的,除了蜜蜂,蚂蚁当仁不让。在复杂多变的蚁巢中, 蚂蚁总是能以最快、最高效的方式游历在各个储藏间(存储食物)。今天,她看完最新一期节目,又发布了一项新任务:小蚁同学,我需要玉米库的玉米,再要配点水果,去帮我找来吧。小蚁正准备出发,蚁后又说:哎呀,回来,我还没说完呢,还有若干要求如下:

1.小蚁同学,你需要尽可能以最少的花费拿到食物(附件图中路线上的数值表示每两个储物间的花费);

2.小蚁同学,你最多只能经过9个储藏间拿到食物(包含起止两个节点,多次通过同一节点按重复次数计算);

3.小蚁同学,你必须经过玉米间,水果间(附件图中标绿色节点);

4.别忘了,食蚁兽也在路上活动呢,一旦与食蚁兽相遇,性命危矣!不过小蚁微信群公告已经公布了敌人信息(附件图中标红色路段);

5.最后,千万别忘了,还有两段路是必须经过的,那里有我准备的神秘礼物等着你呢(附件图中标绿色路段)。

这下小蚁犯难了,这和它们平时找食物的集体活动规则不一样嘛,看来这次需要单独行动了。要怎么选路呢?小蚁经过一番苦思冥想,稿纸堆了一摞,啊,终于找到了!亲爱的同学们,你们能否也设计一种通用的路径搜索算法,来应对各种搜索限制条件,找到一条最优路径,顺利完成蚁后布置的任务呢?

注:

1、蚁巢,有若干个储藏间(附件图中圆圈表示),储藏间之间有诸多路可以到达(各储藏间拓扑图见附件);

2、节点本身通行无花费;

3、该图为无向图,可以正反两方向通行,两方向都会计费,并且花费相同;

4、起止节点分别为附件图中S点和E点。

5、最优路径:即满足限制条件的路径。

解法:

我们的解法中,主要的思想有两个:1.减少约束条件的数量,降低题目难度。将必经路径的两端点等效为必经节点。2.对每个整体路径分段规划。根据必经节点将整体路径分为几个子段,分别计算每个字段的最优路径,然后将各子段得到的路径首尾拼接,花费相加,得到整体路径。

详细做法是这样的:

首先,对约束条件做以下考虑:1.将必经路径的两个端点等效为必经节点。2.计算过程中会暂时将必经路径的花费置为0来保证一定通过该路径,将禁止通行路径的花费置为无穷大(其实是置为了65535)来保证一定不通过该路径。

  1. 记必经路径M条,必经节点N个,必经路径的端点和必经节点重复了P个,那么必经节点的个数Z=2M+N。则有种可能路径,其实条并不准确,因为可能不满足必经路径两端点必须相邻的条件,真正可能的路径条数为:种。
  2. 对这种路径中的每一条,都采取以下计算方法。因为有Z个等效必经节点,所以每条整体路径可以分为Z+1小段来计算各段花费。对于必经节点和必经节点之间的子段,直接调用dij算法求解;对于必经节点和必经路径端点之间的子段,也直接调用dij算法求解;对于必经路径的两端点之间的子段,计算前需要先将该子段花费置为0以保证一定通过该子段,但是计算完该子段后,应立即还原其原有花费,以保证不会影响到其他子段规划的结果;最后还要计算下,初始节点到第一个节点这个子段的路径、最后一个节点到终止节点这个子段的路径。
  3. 将上一步中求得的Z+1条子段首尾拼接就是最终得到的路径,花费也求和保存,用来作对比。
  4. 所有的条路径都这么做完后,比较他们的花费,花费最小的路径就是需要的那条。
  5. 至此找到了花费最小的路径了,但是仅仅由各子段求和作为总花费还不够,还要加上一个必经路径的权值,那是由于必经路径置0导致少计算的花费。
  6. 至此,最优路径得到,最优路径的花费也得到了。

该方法未考虑到的特殊情况,需要做单独处理:

  1. 两条必经路径端点重叠的情况。这种情况导致的结果是:路径中会出现节点重复的现象,但是对花费的计算没有影响。处理方法:在输出时加一个判断,如果连续输出相同的节点,则只输出一个,同时步数少计算一次。
迪杰斯特拉(Dijkstra)算法和普里姆(Prim)算法在图论中都有重要地位,但二者存在明显差异。 ### 功能用途 迪杰斯特拉算法用于解决带权图中的单源最短路径问题,即求图中某一顶点到其他各顶点的最短路径,在网络规划、地图导航等领域有广泛应用,比如在地图导航中计算从一个地点到其他地点的最短路线[^1][^2]。 Prim算法则用于解决最小生成树问题,在电路设计等领域有应用,例如在设计电路时,需要连接多个电子元件,使用Prim算法可以找到连接所有元件的最短电线总长度[^1]。 ### 算法核心思想 迪杰斯特拉算法基于贪心策略,从起始顶点开始,每次选择距离起始顶点最近且未确定最短路径的顶点,然后以该顶点为中间点,更新其他顶点到起始顶点的距离,不断重复这个过程,直到所有顶点的最短路径都被确定[^2]。 Prim算法同样基于贪心策略,从一个初始顶点开始,每次选择与当前生成树相连的边中权值最小的边,并将该边连接的顶点加入到生成树中,不断扩展生成树,直到包含图中的所有顶点[^2]。 ### 代码实现差异 以简单的代码框架来看,在更新距离的部分二者有所不同。在迪杰斯特拉算法中,会更新其他顶点到起始顶点的最短距离,如代码中 `dist[j] = min(dist[j],G[t][j]+dist[j]);` 这一步是迪杰斯特拉算法更新距离的关键操作;而Prim算法在这部分会更侧重于选择与当前生成树相连的最小权值边来扩展生成树,虽然未给出Prim算法代码,但在逻辑上与迪杰斯特拉算法更新距离的侧重点不同[^3]。 ### 复杂度分析 二者的时间复杂度在常见实现下都是 $O(V^2)$,其中 $V$ 是图中顶点的数量。不过,如果使用优先队列等数据结构进行优化,迪杰斯特拉算法可以将时间复杂度优化到 $O((V + E) \log V)$,其中 $E$ 是图中边的数量。Prim算法使用二叉堆优化时,时间复杂度也能达到 $O((V + E) \log V)$。 ### 示例代码(迪杰斯特拉算法) ```python import heapq def dijkstra(graph, start): distances = {node: float('inf') for node in graph} distances[start] = 0 priority_queue = [(0, start)] while priority_queue: current_distance, current_node = heapq.heappop(priority_queue) if current_distance > distances[current_node]: continue for neighbor, weight in graph[current_node].items(): distance = current_distance + weight if distance < distances[neighbor]: distances[neighbor] = distance heapq.heappush(priority_queue, (distance, neighbor)) return distances # 示例图的邻接表表示 graph = { 'A': {'B': 1, 'C': 4}, 'B': {'A': 1, 'C': 2, 'D': 5}, 'C': {'A': 4, 'B': 2, 'D': 1}, 'D': {'B': 5, 'C': 1} } start_node = 'A' result = dijkstra(graph, start_node) print(result) ```
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值