网格文字输入最短路径算法无法通过全部测试用例求助
问题根因定位
你的代码能跑通2*2的单测样例完全是巧合,核心bug集中在输入处理、逻辑鲁棒性两部分,逐个列:
- 宽高读取顺序写反
题目明确输入顺序是先给网格宽度、再给网格高度,你的代码变量定义是先赋值Height再读Width,只要测试用例宽高不相等,整个网格的行列维度直接错误,所有字符坐标计算全乱。你跑通的2*2样例因为宽高相等,刚好没暴露这个问题。 - 待输入文本处理不符合要求
题目规定网格内容之后的所有行都是待输入文本,不是单行,如果你只读取了第一行作为目标串,多行输入的内容会全部丢失;另外你没有过滤无效字符:待输入文本里所有不存在于网格的字符需要直接跳过,不能加入匹配序列,否则递归时找不到对应字符,直接返回错误结果。 - 最小值判断逻辑有严重隐患
你用FinalPath > NewPath ^ FinalPath == -1做最小值判断,属于典型的可读性极差的炫技式代码:C#中逻辑异或^的优先级低于比较运算符,虽然当前场景下计算结果歪打正着,但只要后续修改变量类型、调整判断条件,很容易出现非预期的逻辑结果。 - 递归逻辑无剪枝+重复遍历网格效率极低
每次递归都双层循环遍历整个网格找当前目标字符的位置,没有提前缓存字符到坐标列表的映射,而且DFS过程中没有剪枝:如果当前累计路径长度已经超过已经找到的最短路径,还会继续往下递归,遇到长文本、多重复字符的用例直接超时。 - 额外注意:你贴的代码缩进混乱,大括号对齐有问题,如果实际运行的代码和贴出的缩进一致,会存在作用域错误,直接导致循环逻辑截断。
修复方案
- 先修正输入读取逻辑
- 第一行输入转int赋值给
Width,第二行输入转int赋值给Height - 读取第三行作为网格内容
Content - 循环读取后续所有行,拼接成完整的待输入原始文本
- 预处理原始文本:遍历每个字符,判断是否存在于网格内容中,只保留存在的字符,组成最终的匹配序列
Words,如果过滤后Words长度为0,直接返回0即可
- 第一行输入转int赋值给
- 提前做字符坐标缓存,避免每次递归扫全表
初始化一个字典Dictionary<char, List<(int x, int y)>> charPositions,遍历填充好的网格,把每个字符对应的所有坐标存到对应列表里,后续找目标字符的位置直接查字典就行,不用双层循环。 - 优化最短路径计算逻辑,替换有风险的判断
把异或判断改成直白的if (FinalPath == -1 || NewPath < FinalPath),减少出错概率;递归时如果当前累计路径长度已经大于等于已找到的最短路径,直接return剪枝,不用继续往下递归。
更推荐用动态规划替代纯DFS:每处理一个字符,只需要记录到达该字符每个出现位置的最短路径长度,处理下一个字符时,只需要遍历上一个字符所有位置到当前字符所有位置的曼哈顿距离,更新最短值即可,时间复杂度是O(n*k^2)(n是目标串长度,k是单个字符的最大重复次数),不会出现指数级超时的问题。 - 确认初始调用参数正确
初始位置是网格左上角也就是坐标(0,0),第一次递归的参数是a=0(找第一个目标字符)、FirstX=0、FirstY=0、PathLength=0,不要传错初始坐标。 - 最终总操作数 = 最短移动路径长度 + 过滤后有效目标串的长度(每个字符一次确认点击)
修复后核心逻辑参考
// 预处理字符坐标缓存 Dictionary<char, List<(int x, int y)>> posMap = new Dictionary<char, List<(int x, int y)>>(); for (int i = 0; i < Height; i++) { for (int j = 0; j < Width; j++) { char c = grid[i, j]; if (!posMap.ContainsKey(c)) posMap[c] = new List<(int x, int y)>(); posMap[c].Add((i, j)); } } // DP实现:dp[i][p]表示选第i个字符的第p个位置时的最短移动距离 List<int> prevDp = new List<int>(); // 初始化第一个字符的dp值 var firstPositions = posMap[Words[0]]; foreach (var (x,y) in firstPositions) { prevDp.Add(Math.Abs(0 - x) + Math.Abs(0 - y)); } for (int i = 1; i < Words.Length; i++) { var currPositions = posMap[Words[i]]; List<int> currDp = new List<int>(new int[currPositions.Count]); for (int currIdx = 0; currIdx < currPositions.Count; currIdx++) { var (cx, cy) = currPositions[currIdx]; int minDist = int.MaxValue; for (int prevIdx = 0; prevIdx < prevDp.Count; prevIdx++) { var (px, py) = posMap[Words[i-1]][prevIdx]; int dist = prevDp[prevIdx] + Math.Abs(cx - px) + Math.Abs(cy - py); if (dist < minDist) minDist = dist; } currDp[currIdx] = minDist; } prevDp = currDp; } int minMove = prevDp.Min(); int total = minMove + Words.Length; // 加确认点击次数
拿题目给的3*3样例跑这段逻辑,算出来minMove是8,加7次确认总结果15,和样例输出完全一致。
内容的提问来源于stack exchange,提问作者CodeAche
相关产品推荐
相关产品推荐

