《剑指迷宫:破解矩阵路径之谜》

故事标题:《剑与路之书 —— 矩阵迷宫的路径密钥》


引子:迷宫之城的秘密

在遥远的算法大陆,有一座神秘的城市——“迷宫之城”。在这座城市的中心,矗立着一座名为“命运之塔”的古老建筑。传说中,这里藏着一本神秘的典籍——《剑指天书》,书中记载着无数关于矩阵、路径和逻辑推理的奥秘。

在这片土地上,有一种被称为 “矩阵迷宫” 的古老魔法阵。它由一个个字符格子组成,每一步只能向上下左右移动一格。而最神奇的是,如果一条路径经过了某个格子,就不能再次进入这个格子。例如:

 A B T G
 C F C S
 J D E H

在这个迷宫中,存在一条路径可以走出字符串 "BFCE"

  • BFCE(下标路径)

但同样的迷宫中却无法走出 "ABFB",因为第一个 B 已经被占用了,不能再回头使用。

人们相信,只有找到包含目标字符串所有字符的路径,才能激活迷宫的核心力量,获得通往更深层知识的钥匙。


‍♂️第二幕:年轻的解阵师小海

在一个晨光微露的清晨,年轻的解阵师 小海 带着他的剑与笔记本,走进了“命运之塔”。他立志要解开矩阵迷宫中的路径之谜。

核心咒语(主函数):寻找路径密钥

 bool hasPath(const char* matrix, int rows, int cols, const char* str)
 {
     // 检查输入是否合法:空矩阵或空字符串都视为非法输入
     if(matrix == nullptr || rows < 1 || cols < 1 || str == nullptr)
         return false;
 ​
     bool *visited = new bool[rows * cols]; // 创建一个访问标记数组,初始化为false
     memset(visited, 0, rows * cols);       // 将访问标记数组的所有元素设置为未访问状态
 ​
     int pathLength = 0;                     // 记录当前匹配到的目标字符串的长度
     for(int row = 0; row < rows; ++row)     // 遍历整个矩阵
     {
         for(int col = 0; col < cols; ++col)
         {
             if(hasPathCore(matrix, rows, cols, row, col, str,
                 pathLength, visited))       // 调用核心探索术尝试从当前位置开始匹配
             {
                 delete[] visited;           // 释放访问标记数组
                 return true;                // 找到路径,返回true
             }
         }
     }
 ​
     delete[] visited;                       // 释放访问标记数组
     return false;                           // 未找到路径,返回false
 }

这段咒语召唤出一个核心探索术 hasPathCore,用于在迷宫中递归探索路径。

辅助阵法(回溯术):深入迷宫

// 回溯算法原理注释:
 // 回溯是一种通过构建所有可能的解决方案,并逐步排除不符合条件的方案来解决问题的策略。
 // 在此情境中,我们试图找到一条从矩阵任意位置出发,能够顺序匹配给定字符串的路径。
 // 如果当前步骤不能继续前进,则撤销前一步的选择(即回退),并尝试其他可能的方向。
 bool hasPathCore(const char* matrix, int rows, int cols, int row,
     int col, const char* str, int& pathLength, bool* visited)
 {
     // 如果已经匹配到了目标字符串的末尾,则返回true
     if(str[pathLength] == '\0')
         return true;
 ​
     bool hasPath = false;
     // 检查当前位置是否有效且未访问,并且匹配目标字符串的当前字符
     if(row >= 0 && row < rows && col >= 0 && col < cols
         && matrix[row * cols + col] == str[pathLength]
         && !visited[row * cols + col])
     {
         ++pathLength;                      // 匹配成功,增加匹配长度
         visited[row * cols + col] = true;  // 标记当前位置已访问
 ​
         // 尝试向四个方向继续搜索
         hasPath = hasPathCore(matrix, rows, cols, row, col - 1, str, pathLength, visited) // 向左
             || hasPathCore(matrix, rows, cols, row - 1, col, str, pathLength, visited)    // 向上
             || hasPathCore(matrix, rows, cols, row, col + 1, str, pathLength, visited)    // 向右
             || hasPathCore(matrix, rows, cols, row + 1, col, str, pathLength, visited);   // 向下
 ​
         // 如果未能找到路径,则回退并取消当前位置的访问标记
         if(!hasPath)
         {
             --pathLength;                   // 回退匹配长度
             visited[row * cols + col] = false; // 取消访问标记
         }
     }
 ​
     return hasPath;
 }

在这段代码中,回溯算法的工作原理是这样的:

  1. 选择路径:从矩阵的一个点开始尝试,看看能否匹配字符串的第一个字符。

  2. 递归探索:一旦匹配成功,递归地尝试从该点出发的四个方向(上下左右)继续匹配下一个字符。

  3. 回退机制:如果某条路径走不通(比如到达了死胡同或者无法匹配剩余的字符),则撤销前一步的选择,回到上一个节点,尝试其他可能的路径。

  4. 终止条件:当成功匹配完字符串的所有字符时,表示找到了有效的路径;否则,在所有可能性都被尝试后仍无法找到路径,则表示不存在符合条件的路径。


️第三幕:十二道试炼门 —— 迷宫的边界条件探索

实际案例分析

对话场景1:小海与导师讨论

小海:“导师,我在思考如何处理那些重复字符的问题,比如这个矩阵中有很多相同的字符。”

导师:“很好,这是个关键点。你需要确保每次遍历时不会重复访问同一个位置。你现在的做法是正确的,记得每次成功匹配后都要标记该位置为已访问,一旦发现此路径不通则需要回退。”

测试案例1:标准路径

输入矩阵

 A B T G
 C F C S
 J D E H

目标字符串"BFCE" 输出:✅ 成功找到路径!

阵法顺利解开!从 B 开始,依次找到 F, C, 和 E

对话场景2:小海与朋友探讨

小海的朋友:“小海,我注意到有些情况下即使矩阵中有足够的字符,但由于它们不能形成连续的路径,也无法匹配。”

小海:“确实如此,这就是为什么我们需要仔细检查每个字符周围的邻居,并确保没有重复访问同一个位置。”

测试案例2:路径重复但合法

输入矩阵

 A B C E
 S F C S
 A D E E

目标字符串"SEE" 输出:✅ 成功找到路径!

尽管有两个 E,只要不重复走同一个格子即可!

测试案例3:路径重复且非法

输入矩阵

 A B T G
 C F C S
 J D E H

目标字符串"ABFB" 输出:❌ 路径冲突,无法完成!

第一个 B 已经被占用,无法回头!

测试案例4:长路径成功

输入矩阵

 A B C E H J I G
 S F C S L O P Q
 A D E E M N O E
 A D I D E J F M
 V C E I F G G S

目标字符串"SLHECCEIDEJFGGFIE" 输出:✅ 成功找到路径!

多层回溯,最终寻得真相!

对话场景3:小海总结经验

小海:“我发现,处理这些复杂情况的关键在于理解回溯的概念,即当我们遇到死胡同时,必须能够回到上一步,重新选择不同的路径。”

导师:“非常好,正是这种思维让你能够在面对任何挑战时都不慌不忙,找到解决之道。”


第四幕:智慧的传承

夕阳西下,小海站在“命运之塔”的顶端,手捧《剑指天书》,成为新一代的“迷宫宗师”。

他向后来者讲述自己的经历:

“每一个矩阵迷宫,都是一个世界;每一段代码,都是一把钥匙。只有理解它的规则,掌握它的边界条件,你才能真正驾驭它。”

孩子们围坐在他身边,认真聆听,眼中闪烁着对未来的憧憬。


结语:剑与路的传说

从此,“矩阵迷宫的路径密钥”不仅成为了解阵术的象征,也成为了每一位踏上算法之路的旅者必经的一课。

“当你面对迷宫,不要慌张;只需冷静分析,就能从路径中找出通往真理的道路。” —— 解阵师小海,留于《剑指天书·卷十四》


附录:完整代码实现(含详细注释)及更多测试案例

 // 更多测试案例...
 void Test(const char* testName, const char* matrix, int rows, int cols, const char* str, bool expected)
 {
     printf("%s begins: ", testName);
     if(hasPath(matrix, rows, cols, str) == expected)
         printf("Passed.\n");
     else
         printf("FAILED.\n");
 }
 ​
 int main()
 {
     // 测试案例...
     Test("Test1", "ABTGCFCSJDEH", 3, 4, "BFCE", true);
     Test("Test2", "ABCESFCSADEE", 3, 4, "SEE", true);
     Test("Test3", "ABTGCFCSJDEH", 3, 4, "ABFB", false);
     Test("Test4", "ABCEHJIGSFCSLOPQADEEMNOEADIDEJFMVCEIFGGS", 5, 8, "SLHECCEIDEJFGGFIE", true);
     Test("Test5", "ABCEHJIGSFCSLOPQADEEMNOEADIDEJFMVCEIFGGS", 5, 8, "SGGFIECVAASABCEHJIGQEM", true);
     Test("Test6", "ABCEHJIGSFCSLOPQADEEMNOEADIDEJFMVCEIFGGS", 5, 8, "SGGFIECVAASABCEEJIGOEM", false);
     Test("Test7", "ABCEHJIGSFCSLOPQADEEMNOEADIDEJFMVCEIFGGS", 5, 8, "SGGFIECVAASABCEHJIGQEMS", false);
     Test("Test8", "AAAAAAAAAAAA", 3, 4, "AAAAAAAAAAAA", true);
     Test("Test9", "AAAAAAAAAAAA", 3, 4, "AAAAAAAAAAAAA", false);
     Test("Test10", "A", 1, 1, "A", true);
     Test("Test11", "A", 1, 1, "B", false);
     Test("Test12", nullptr, 0, 0, nullptr, false);
 ​
     return 0;
 }

你可能感兴趣的:(故事版本数据结构与算法,C++,数据结构,算法,递归,回溯)