讲师中心 微信公众号
AI工具推荐 视频效率加速

如何用 DFS 正确求解迷宫滚动球的最短路径问题

酷强小哥_3868

酷强小哥_3868

发布时间:2026-06-30 21:12:36

|

192人浏览过

|

来源于php中文网

原创

本文详解为何朴素 dfs 无法保证找到 maze ii 类滚动球问题的最短路径,并给出两种关键改进方案:基于方向状态的 visited 三维标记与带距离剪枝的动态更新机制。

本文详解为何朴素 dfs 无法保证找到 maze ii 类滚动球问题的最短路径,并给出两种关键改进方案:基于方向状态的 visited 三维标记与带距离剪枝的动态更新机制。

在图论与算法实践中,DFS(深度优先搜索)常被误用于求解“最短路径”问题——尤其在 LeetCode 的 Maze II 这类滚动球迷宫题中。题目要求球从起点出发,沿上下左右四个方向持续滚动直至撞墙停止,每次停驻点构成一个有效状态;目标是找到抵达终点的最小滚动步数(即经过的空格总数)。虽然 BFS 天然适配无权图最短路径,但许多开发者尝试用 DFS 实现,却屡次得到错误结果(如示例输入应输出 12,而原始代码返回 16)。根本原因在于:标准 DFS 缺乏对“同一位置不同进入方向”状态的区分能力,且未对非最优路径进行及时剪枝。

❌ 原始 DFS 的致命缺陷

原始实现使用二维 visited[i][j] 标记已访问坐标,一旦某坐标 (i, j) 被访问过,后续任何方向滚入该点都会被跳过。然而,在滚动模型中,从不同方向到达同一坐标,代表完全不同的状态——因为下一步可选的滚动方向受当前“来向”影响(例如,从上方滚入后不能立即向上反向滚动,但可向左/右/下继续),更重要的是:更晚到达某点的路径,可能对应更小的累计距离。二维 visited 粗暴阻断了所有后续可能性,导致更优路径被提前扼杀。

此外,原始代码在进入递归前未做任何距离判断,即使当前 count 已远超已知最优解,仍继续深搜,造成大量无效计算。

✅ 改进方案一:三维 visited —— 按“到达方向”精细化状态

关键洞察:每个停驻点 (x, y) 需记录 以哪个方向(0: 上,1: 下,2: 左,3: 右)滚入 时的访问状态。因此将 visited 升级为三维布尔数组 visited[x][y][dirIdx]:

boolean[][][] visited = new boolean[maze.length][maze[0].length][4];
// 在 dfs 中检查并标记
if (!visited[x][y][k]) {
    visited[x][y][k] = true;
    dfs(maze, x, y, destination, visited, newcount);
    visited[x][y][k] = false; // 回溯(若需复用状态)
}

此设计确保:(2,3) 点从上方滚入(dirIdx=0)和从左侧滚入(dirIdx=2)被视为两个独立状态,互不干扰。这解决了状态覆盖问题,使 DFS 能探索所有合法路径分支。

✅ 改进方案二:距离驱动剪枝 —— 动态更新最优到达代价

更进一步,我们不仅需要知道“是否来过”,更要记录“以某方向到达 (x,y) 的最小步数”。于是将 visited 替换为 dist[x][y][dirIdx],初始化为 Integer.MAX_VALUE:

int[][][] dist = new int[maze.length][maze[0].length][4];
for (int i = 0; i < maze.length; i++) {
    for (int j = 0; j < maze[0].length; j++) {
        Arrays.fill(dist[i][j], Integer.MAX_VALUE);
    }
}
// 在 dfs 中剪枝
if (newcount < dist[x][y][k]) {
    dist[x][y][k] = newcount;
    dfs(maze, x, y, destination, dist, newcount);
}

该策略实现了 Dijkstra 式的距离松弛:仅当发现更短路径到达 (x,y) 的某个方向状态时,才继续递归。这大幅减少搜索空间,避免陷入长路径陷阱,是 DFS 求解最短路的核心优化。

⚠️ 注意事项与总结

  • DFS ≠ 最短路径算法:除非辅以状态去重与距离剪枝,否则 DFS 仅保证可达性,不保证最优性。
  • 状态定义决定正确性:滚动球问题的状态必须包含 (行, 列, 入口方向) 三元组,缺一不可。
  • 剪枝优于回溯:在递归入口处用 if (newcount >= dist[x][y][k]) return; 直接剪枝,比回溯标记更高效。
  • 实际推荐方案:尽管改进后 DFS 可行,但 Maze II 的本质是边权为正的无向图最短路径问题,优先选用 Dijkstra(堆优化)或 BFS(因边权恒为1,但注意:此处“边权”是滚动距离,非单位步,故严格需 Dijkstra)。DFS 改进版更多用于理解状态建模思想。

最终,正确实现的 DFS 不再是盲目遍历,而是以状态空间建模为基石、以距离优化为引擎的精确搜索——这正是图算法从“能跑通”迈向“可证明正确”的关键跃迁。

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热门AI工具

更多
DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

SkildArt
SkildArt Hot

SkildArt是一款AI文本写作工具,一站式 AI 视觉创作平台。

PixPix
PixPix Hot

PixPix是一款面向电商视觉生产的AI商品图生成工具。

VibeKnow
VibeKnow Hot

一款AI视频创作工具,主要用于全球首个AI知识视频创作平台,文档、文章、网页,一键生成视频,适合需要提升相关任务效率的用户。

咔片AIPPT

一款在线AI演示文稿制作工具,可根据主题和内容需求辅助生成PPT结构与页面,提高演示材料制作效率。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

蛙蛙写作

一款AI论文写作工具,主要用于超级AI智能写作助手,适合需要提升相关任务效率的用户。

Atoms
Atoms Hot

Atoms是一款AI智能体工具,第一支自动构建真实业务的 AI 团队。

相关专题

更多
PixTV官网入口地址合集
PixTV官网入口地址合集

本专题汇总了 PixTV AI 一站式视频创作平台的官方入口与使用教程。无需下载软件,浏览器直接访问即可使用。平台将剧本、图像、视频、声音与剪辑整合在“无限画布”中,接入 GPT Image 2.5、Seedance 2.5 等头部模型。本专题整理了从新建画布、角色锚定、分镜拆分到视频生成与导出的完整操作指南,助你快速上手 AI 短剧与漫剧创作。

20

2026.10.10

Kratos框架HTTP与gRPC服务开发教程
Kratos框架HTTP与gRPC服务开发教程

本专题围绕Kratos框架双协议服务开发,涵盖HTTP路由与处理器编写、参数获取、gRPC服务实现与客户端调用、metadata上下文传递、encoding编解码注册、统一响应封装、超时控制与流式响应实现方法。

20

2026.10.10

Kratos框架Protobuf接口定义与代码生成合集
Kratos框架Protobuf接口定义与代码生成合集

本专题讲解Kratos框架接口定义体系,涵盖proto编写规范、proto add/client/server生成命令、http注解路由、validate校验、OpenAPI文档生成、跨服务proto复用与兼容性设计。

0

2026.10.10

C++虚函数怎么定义和调用
C++虚函数怎么定义和调用

C++虚函数是实现运行时多态的重要机制。本专题从virtual关键字的基本用法入手,介绍基类与派生类之间的函数重写、基类指针调用派生类方法,以及动态绑定的执行过程,帮助初学者掌握虚函数的核心语法。

20

2026.10.10

C++类与对象的封装方法教程
C++类与对象的封装方法教程

C++封装是面向对象编程的核心特性之一,通过类将数据与操作数据的函数组织在一起,并利用访问权限控制外部访问。本专题介绍类的定义、成员变量、成员函数以及public、private和protected的使用方法,帮助初学者掌握封装的基本原理。

20

2026.10.10

C++构造函数定义与调用方法
C++构造函数定义与调用方法

C++构造函数用于初始化类对象,是面向对象编程的重要基础。本专题从构造函数的定义、声明和调用入手,介绍默认构造函数、带参数构造函数、拷贝构造函数及成员初始化列表,帮助初学者掌握对象创建与初始化的基本方法。

20

2026.10.10

Kratos框架零基础入门教程
Kratos框架零基础入门教程

本专题整理Kratos框架入门内容,涵盖Go环境准备、kratos CLI安装升级、new命令创建项目、目录结构分层说明、服务启动与双协议端口、依赖下载报错排查,帮助开发者快速跑通第一个Kratos框架微服务应用。

20

2026.10.10

C++条件判断语句怎么写
C++条件判断语句怎么写

C++条件判断是控制程序执行流程的重要基础。本专题介绍if、if-else、else if和switch等常见分支语句,结合条件表达式、比较运算符与代码示例,帮助初学者掌握不同场景下的判断逻辑。

0

2026.10.10

C++变量怎么声明和赋值
C++变量怎么声明和赋值

C++变量是编写程序和存储数据的基础。本专题围绕变量声明、定义、初始化、赋值和类型选择等内容展开,帮助初学者理解不同变量的用法,并掌握在实际代码中定义和使用变量的方法。

20

2026.10.10

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
热门推荐
/
最新课程
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn