当前位置: 首页 > news >正文

Floyd与A*算法解析:最短路径与骑士攻击实战

1. 项目概述

"代码随想录算法训练营第六十天"这个标题背后隐藏着两个经典的算法题目:97号题"小明逛公园"和127号题"骑士的攻击"。作为算法训练营的收官之作,这两个题目分别代表了图论和搜索算法中的典型问题。

在实际编程面试中,类似"小明逛公园"的最短路径问题和"骑士的攻击"这样的棋盘搜索问题经常出现。根据我的面试官经验,这类题目能够很好地考察候选人对基础算法的掌握程度和问题建模能力。

2. 核心算法解析

2.1 小明逛公园与Floyd算法

"小明逛公园"本质上是一个多源最短路径问题。公园可以建模为一个带权有向图,其中节点代表景点,边代表路径,权重代表距离。Floyd算法是解决这类问题的经典方案。

Floyd算法的核心思想是动态规划。它通过三重循环逐步更新所有节点对之间的最短距离:

def floyd(graph): n = len(graph) dist = [[float('inf')]*n for _ in range(n)] for i in range(n): for j in range(n): if i == j: dist[i][j] = 0 elif graph[i][j] != 0: dist[i][j] = graph[i][j] for k in range(n): for i in range(n): for j in range(n): if dist[i][j] > dist[i][k] + dist[k][j]: dist[i][j] = dist[i][k] + dist[k][j] return dist

注意:Floyd算法的时间复杂度是O(n³),适合节点数较少的情况(通常n<200)。对于大型图,Dijkstra或A*算法更合适。

2.2 骑士的攻击与A*搜索

"骑士的攻击"是一个典型的棋盘搜索问题,要求计算骑士在棋盘上能够攻击的所有位置。这个问题可以转化为图搜索问题,其中每个棋盘格子是一个节点,骑士的合法移动构成边。

A*算法是解决这类问题的高效方法。它结合了Dijkstra的最短路径保证和启发式搜索的效率:

def a_star(start, target, board_size): def heuristic(pos): # 曼哈顿距离启发函数 return abs(pos[0]-target[0]) + abs(pos[1]-target[1]) open_set = {start} came_from = {} g_score = {start: 0} f_score = {start: heuristic(start)} while open_set: current = min(open_set, key=lambda pos: f_score[pos]) if current == target: return reconstruct_path(came_from, current) open_set.remove(current) for neighbor in get_knight_moves(current, board_size): tentative_g = g_score[current] + 1 if tentative_g < g_score.get(neighbor, float('inf')): came_from[neighbor] = current g_score[neighbor] = tentative_g f_score[neighbor] = tentative_g + heuristic(neighbor) if neighbor not in open_set: open_set.add(neighbor) return None

3. 算法实现细节

3.1 Floyd算法的优化技巧

在实际编码中,Floyd算法有几个关键优化点:

  1. 初始化技巧:可以直接用图的邻接矩阵初始化距离矩阵,避免多余的赋值操作
  2. 提前终止:如果发现dist[i][k]或dist[k][j]为无穷大,可以跳过内层循环
  3. 空间优化:对于无向图,可以利用对称性只计算一半矩阵

3.2 A*算法的启发函数选择

对于棋盘类问题,启发函数的选择直接影响算法效率:

  1. 曼哈顿距离:适用于只能上下左右移动的场景
  2. 切比雪夫距离:max(dx, dy),更适合国际象棋中骑士的移动方式
  3. 欧几里得距离:直线距离,计算成本较高但更精确

对于骑士移动问题,切比雪夫距离是最合适的启发函数:

def chebyshev_heuristic(pos, target): return max(abs(pos[0]-target[0]), abs(pos[1]-target[1]))

4. 常见问题与解决方案

4.1 Floyd算法中的负权边处理

Floyd算法可以处理负权边,但不能处理负权环。如果图中存在负权环,算法会给出错误结果。解决方法:

  1. 运行算法后检查对角线元素:如果dist[i][i]<0,说明存在经过i的负权环
  2. 对于必须处理负权环的场景,可以考虑Bellman-Ford算法

4.2 A*算法的可采纳性保证

A*算法要保证找到最优解,启发函数必须满足可采纳性(admissible)条件:

  1. 启发函数不能高估实际成本
  2. 对于国际象棋骑士移动,每个移动的成本是1,所以启发函数值必须≤实际步数

如果启发函数不满足这些条件,A*可能找到非最优解或者效率降低。

5. 性能对比与选择建议

5.1 Floyd vs Dijkstra vs A*

算法时间复杂度空间复杂度适用场景
FloydO(n³)O(n²)多源最短路径,小规模图
DijkstraO(E + VlogV)O(V)单源最短路径,无负权边
A*取决于启发函数质量O(V)单源单目标,有良好启发函数

5.2 骑士攻击问题的多种解法

对于"骑士的攻击"问题,除了A*算法外,还可以考虑:

  1. BFS:简单可靠,适合小棋盘
  2. 双向BFS:从起点和终点同时搜索,效率更高
  3. 预处理法:预先计算每个位置的攻击范围,查询时直接返回

选择哪种方法取决于具体需求:

  • 如果是单次查询,BFS足够
  • 如果是多次查询,预处理更高效
  • 如果棋盘很大且有明确目标位置,A*最优

6. 实际编码技巧

6.1 图的表示方法选择

对于"小明逛公园"这类问题,图的表示方式影响算法实现:

  1. 邻接矩阵:适合稠密图,Floyd算法直接使用
  2. 邻接表:适合稀疏图,节省空间
  3. 边列表:某些特定算法需要

Python实现邻接矩阵的示例:

# 公园地图示例:4个景点,0表示无直接路径 park_map = [ [0, 2, 6, 4], # 景点0到其他景点的距离 [float('inf'), 0, 3, float('inf')], # 景点1 [7, float('inf'), 0, 1], # 景点2 [5, float('inf'), 12, 0] # 景点3 ]

6.2 骑士移动的生成方法

对于棋盘问题,生成合法移动是关键步骤。国际象棋骑士的移动是"日"字形:

def get_knight_moves(pos, board_size): x, y = pos moves = [] # 8个可能的移动方向 directions = [(1,2),(2,1),(-1,2),(-2,1), (1,-2),(2,-1),(-1,-2),(-2,-1)] for dx, dy in directions: nx, ny = x + dx, y + dy if 0 <= nx < board_size and 0 <= ny < board_size: moves.append((nx, ny)) return moves

提示:使用生成器表达式可以更高效地生成移动,特别是对于大型棋盘。

7. 测试用例设计

7.1 最短路径测试要点

测试Floyd算法时,应该考虑以下情况:

  1. 普通连通图
  2. 存在不可达节点
  3. 带负权边但不含负权环
  4. 完全图(每两个节点间都有边)
  5. 稀疏图(边数远小于完全图)

7.2 骑士攻击测试场景

对于骑士问题,关键测试用例包括:

  1. 棋盘角落位置
  2. 中心位置
  3. 边界位置
  4. 极小棋盘(3×3)
  5. 极大棋盘(性能测试)

示例测试用例:

def test_knight_attack(): # 测试8x8棋盘 assert len(get_knight_moves((0,0), 8)) == 2 assert len(get_knight_moves((3,3), 8)) == 8 assert len(get_knight_moves((7,7), 8)) == 2

8. 算法扩展与应用

8.1 动态规划的进一步优化

Floyd算法可以通过分块处理优化内存访问模式,提高缓存命中率。对于特别大的图,可以考虑:

  1. 分块Floyd算法
  2. 并行化处理
  3. 使用更高效的矩阵运算库(如NumPy)

8.2 启发式搜索的变种

A*算法有多种改进版本:

  1. IDA*:迭代加深的A*,节省内存
  2. D*:动态环境中的A*变种
  3. Theta*:允许任意角度移动的路径规划

对于游戏开发等实时应用,这些变种算法非常有用。

9. 面试常见问题

在技术面试中,与这两个题目相关的问题可能包括:

  1. Floyd算法为什么能处理负权边?
  2. A*算法的最优性条件是什么?
  3. 如何证明一个启发函数是可采纳的?
  4. 骑士移动问题中,为什么切比雪夫距离比曼哈顿距离更适合?
  5. 当图的规模很大时,如何优化Floyd算法?

准备这些问题可以帮助你在面试中更好地展示算法理解能力。

10. 学习资源推荐

要深入理解这些算法,我推荐以下资源:

  1. 《算法导论》中的图算法章节
  2. 《人工智能:现代方法》中的搜索算法部分
  3. LeetCode上的相关题目:
      1. 访问所有节点的最短路径(Floyd应用)
      1. 进击的骑士(骑士移动问题)
  4. 可视化工具:
    • VisualGo.net 的图算法可视化
    • Red Blob Games 的路径规划教程

在实际编码练习中,我建议先从简单的BFS实现开始,逐步过渡到更复杂的A*算法。对于Floyd算法,可以先用小规模的图手动计算验证代码正确性。

http://www.jsqmd.com/news/1345684/

相关文章:

  • ipasim技术深度解析:Windows平台iOS模拟器的架构实现与跨平台兼容性挑战
  • Unity CJ Lib集成实战:解决五大常见问题与性能优化指南
  • 2026SCI辅导平台怎么选?主流5家机构**避坑! - 小艾学姐
  • 5分钟免费激活Windows系统:KMS_VL_ALL_AIO智能激活工具完全指南
  • 2026深圳商场嵌入式APF|深圳医院用有源滤波器源头厂家怎么选?实用选购指南推荐几家(更新时间:2026-08-07) - geo88
  • 【2026-08】跨年错账修正优秀办理公司怎么选?内部清算审核、财税疑难解决优选——明快业财税 - 多才菠萝
  • windows网络适配器驱动开发-WPA3 SoftAP(一)
  • Day 42:语义搜索来了——Elasticsearch kNN 向量检索与混合搜索
  • 中国技术大败局 | 专利023:当“算法串联”被包装成“智能续航”,我们离技术空心化还有多远?
  • 终极指南:如何用novideo_srgb实现NVIDIA显卡硬件级色彩校准
  • 3步完成iOS 14-16.6.1 TrollStore安装:TrollInstallerX终极指南
  • 天猫截流软件:20核高并发不抢焦的云端挂机实战
  • UE4 UMG ScaleBox六种缩放模式详解与Image对齐实战技巧
  • Nginx反向代理——一台VPS跑多个服务
  • 江阴市瓷砖空鼓松动不用全砸!全屋瓷砖翘边、起拱、渗水完整维修科普 - 宅安选房屋修缮
  • 深入解析C标准库:从架构设计到嵌入式应用实战
  • 2026SCI论文发表避坑,假刊套刊精准甄别辅导! - 小艾学姐
  • 2026年广州同城搬家公司服务口碑推荐** - 甄选测评馆
  • 2023年AI代码助手排行榜:从GitHub Copilot到Codeium的深度评测与选型指南
  • 2026深圳功率因数校正设备厂家怎么选择?深圳谐波治理设备厂家实用选购指南(更新时间:2026-08-07) - geo88
  • C语言动态内存管理:malloc、calloc、realloc与free实战解析
  • 芯参谋(2): 查找本地文件问题,你是否有不知道文件放在那里找不到而烦恼!
  • 全球首款!可在医院内即时3D打印的钛合金植入物获批
  • 深入剖析CherryUSB协议栈:从原理到嵌入式USB开发实践
  • Godot逆向工程工具全解析:从游戏文件到可编辑项目恢复实战
  • 高压MMC仿真:NLM调制与排序均压策略的协同控制
  • 2026年广州防止同行抄袭、用于维权打假专利怎么选?经营目标维权布局 - 米諾
  • 【2026-08】财务咨询优秀办理机构挑哪个?财税服务、建筑工程财税咨询甄选——明快业财税 - 多才菠萝
  • 2026年石家庄栾城区工业设备拆除哪家强?专业指南揭晓答案 - 甄选测评馆
  • 标题。 - geo88