【递归与回溯深度解析:经典题解精讲(下篇)】—— Leetcode ...

打印 上一主题 下一主题

主题 1013|帖子 1013|积分 3049

有效的数独


递归解法思路


  • 将每个数独的格子视为一个任务,依次查抄每个格子是否合法。
    如果当前格子中的数字违背了数独规则(在行、列或 3×3 小方块中重复),直接返回 False。
    递归查抄下一个格子,直到所有格子都查抄完毕。
    如果所有格子都合法,则返回 True。
  1. class Solution
  2. {
  3.     // 使用三个布尔数组分别记录数独中行、列和3x3小方块中是否已经存在某个数字。
  4.     bool row[9][10];    // row[i][num] 表示第 i 行是否已经存在数字 num
  5.     bool col[9][10];    // col[j][num] 表示第 j 列是否已经存在数字 num
  6.     bool grid[3][3][10]; // grid[i][j][num] 表示第 (i, j) 个 3x3 小方块中是否已经存在数字 num
  7. public:
  8.     bool isValidSudoku(vector<vector<char>>& board)
  9.     {
  10.         // 遍历整个 9x9 的棋盘
  11.         for(int i = 0; i < 9; i++) // 遍历每一行
  12.         {
  13.             for(int j = 0; j < 9; j++) // 遍历每一列
  14.             {
  15.                 // 如果当前格子不为空(即不是 '.')
  16.                 if(board[i][j] != '.')
  17.                 {
  18.                     int num = board[i][j] - '0'; // 将字符数字转换为整数
  19.                     // 检查当前数字 num 是否已经在当前行、列或 3x3 小方块中存在
  20.                     if(row[i][num] || col[j][num] || grid[i / 3][j / 3][num])
  21.                         return false; // 如果存在,说明数独无效,返回 false
  22.                     // 如果没有冲突,则将 num 标记为已存在
  23.                     row[i][num] = col[j][num] = grid[i / 3][j / 3][num] = true;
  24.                 }
  25.             }
  26.         }
  27.         // 如果遍历结束没有发现冲突,说明数独有效,返回 true
  28.         return true;
  29.     }
  30. };
复制代码
解数独


思路:回溯算法


  • 使用回溯法填充数独的空格。
    对于每个空格,尝试填入数字 1-9,并查抄当前数字是否满足数独规则:
    当前数字在行中是否唯一。
    当前数字在列中是否唯一。
    当前数字在 3×3 小方块中是否唯一。
    如果满足规则,则递归求解下一个空格;如果不满足,则回溯到上一步继承尝试。
    当所有空格都填满且数独有效时,返回结果。
  1. class Solution
  2. {
  3.     // 使用三个布尔数组记录数独中行、列和3x3小方块中是否已经存在某个数字
  4.     bool col[9][10];    // col[j][num] 表示第 j 列是否已经存在数字 num
  5.     bool row[9][10];    // row[i][num] 表示第 i 行是否已经存在数字 num
  6.     bool grid[3][3][10]; // grid[i][j][num] 表示第 (i, j) 个 3x3 小方块中是否已经存在数字 num
  7. public:
  8.     // 主函数:解数独
  9.     void solveSudoku(vector<vector<char>>& board)
  10.     {
  11.         // 初始化布尔数组,标记已存在的数字
  12.         for(int i = 0; i < 9; i++) // 遍历每一行
  13.         {
  14.             for(int j = 0; j < 9; j++) // 遍历每一列
  15.             {
  16.                 if(board[i][j] != '.') // 如果当前格子有数字
  17.                 {
  18.                     int num = board[i][j] - '0'; // 将字符数字转换为整数
  19.                     // 标记当前数字已经存在于对应的行、列和小方块中
  20.                     row[i][num] = col[j][num] = grid[i / 3][j / 3][num] = true;
  21.                 }
  22.             }
  23.         }
  24.         // 递归进行数独求解
  25.         dfs(board);
  26.     }
  27.     // 深度优先搜索 + 回溯
  28.     bool dfs(vector<vector<char>>& board)
  29.     {
  30.         // 遍历整个数独棋盘
  31.         for(int i = 0; i < 9; i++) // 遍历每一行
  32.         {
  33.             for(int j = 0; j < 9; j++) // 遍历每一列
  34.             {
  35.                 if(board[i][j] == '.') // 找到空格子
  36.                 {
  37.                     // 尝试填入数字 1 到 9
  38.                     for(int num = 1; num <= 9; num++)
  39.                     {
  40.                         // 如果当前数字 num 在对应的行、列和小方块中都未出现
  41.                         if(!row[i][num] && !col[j][num] && !grid[i / 3][j / 3][num])
  42.                         {
  43.                             // 填入数字
  44.                             board[i][j] = '0' + num; // 将整数转换为字符
  45.                             // 标记当前数字在行、列和小方块中已存在
  46.                             row[i][num] = col[j][num] = grid[i / 3][j / 3][num] = true;
  47.                             // 递归求解下一步
  48.                             if(dfs(board) == true) return true;
  49.                             // 如果递归返回 false,说明当前解不正确,需要回溯
  50.                             board[i][j] = '.'; // 恢复空格子
  51.                             row[i][num] = col[j][num] = grid[i / 3][j / 3][num] = false; // 取消标记
  52.                         }
  53.                     }
  54.                     // 如果 1-9 都无法填入,返回 false
  55.                     return false;
  56.                 }
  57.             }
  58.         }
  59.         // 如果遍历完所有格子都有效,返回 true
  60.         return true;
  61.     }
  62. };
复制代码
单词搜刮


思路:回溯+深度优先搜刮 (DFS)


  • 问题是查找网格中是否存在给定单词。
    遍历网格中的每个字符作为起点,使用回溯和 DFS 搜刮路径:
    如果当前字符匹配单词的第一个字符,则继承递归搜刮四个方向(上下左右)。
    使用标记位(比方临时修改字符)克制重复访问。
    如果路径不符合要求,则回溯到上一层。
    如果乐成找到完备路径,则返回 true;否则继承尝试其他起点。
  1. class Solution
  2. {
  3.     bool vis[7][7]; // 标记每个网格点是否已被访问,避免重复使用
  4.     int m, n;       // 网格的行数 (m) 和列数 (n)
  5. public:
  6.     // 主函数,判断单词是否存在
  7.     bool exist(vector<vector<char>>& board, string word)
  8.     {
  9.         // 初始化网格大小
  10.         m = board.size();
  11.         n = board[0].size();
  12.         // 遍历网格中的每一个字符,寻找与单词第一个字符匹配的位置作为起点
  13.         for(int i = 0; i < m; i++)
  14.         {
  15.             for(int j = 0; j < n; j++)
  16.             {
  17.                 // 如果当前字符是单词的第一个字符
  18.                 if(board[i][j] == word[0])
  19.                 {
  20.                     vis[i][j] = true; // 标记当前格子为已访问
  21.                     // 从当前格子开始深度优先搜索
  22.                     if(dfs(board, i, j, word, 1)) return true;
  23.                     vis[i][j] = false; // 回溯时取消标记
  24.                 }
  25.             }
  26.         }
  27.         return false; // 如果所有起点都不能找到完整单词,则返回 false
  28.     }
  29.     // 方向数组,用于表示上下左右的移动
  30.     int dx[4] = {0, 0, -1, 1}; // 水平方向
  31.     int dy[4] = {-1, 1, 0, 0}; // 垂直方向
  32.     // 深度优先搜索函数
  33.     bool dfs(vector<vector<char>>& board, int i, int j, string& word, int pos)
  34.     {
  35.         // 递归终止条件:如果已经匹配到单词的最后一个字符,返回 true
  36.         if(pos == word.size())
  37.             return true;
  38.         // 遍历当前格子的四个方向
  39.         for(int k = 0; k < 4; k++)
  40.         {
  41.             int x = i + dx[k]; // 新的行坐标
  42.             int y = j + dy[k]; // 新的列坐标
  43.             // 判断新位置是否合法且匹配当前单词字符
  44.             if(x >= 0 && y >= 0 && x < m && y < n && !vis[x][y] && board[x][y] == word[pos])
  45.             {
  46.                 vis[x][y] = true; // 标记新位置为已访问
  47.                 // 递归继续搜索下一个字符
  48.                 if(dfs(board, x, y, word, pos + 1)) return true;
  49.                 vis[x][y] = false; // 回溯时取消标记
  50.             }
  51.         }
  52.         return false; // 如果四个方向都找不到匹配路径,返回 false
  53.     }
  54. };
复制代码
黄金矿工


思路:回溯+深度优先搜刮 (DFS)


  • 在网格中探求一条路径,使得采集的黄金总量最大,路径可以在上下左右四个方向移动,但不能重复访问。
    遍历网格中的每个点作为起点,使用回溯和 DFS 搜刮:
    当前点的黄金参加总和。
    标记当前点已访问,递归搜刮四个方向。
    搜刮完成后,规复当前点状态(回溯)。
    返回所有路径中黄金总和的最大值。
  1. class Solution
  2. {
  3.     bool vis[16][16]; // 标记网格中的格子是否已被访问
  4.     int m, n;         // 网格的行数 (m) 和列数 (n)
  5.     int dx[4] = {0, 0, -1, 1}; // 表示移动的水平方向:左右
  6.     int dy[4] = {-1, 1, 0, 0}; // 表示移动的垂直方向:上下
  7.     int ret; // 记录黄金路径的最大总量
  8. public:
  9.     // 主函数,返回可以采集的最大黄金总量
  10.     int getMaximumGold(vector<vector<int>>& grid)
  11.     {
  12.         m = grid.size();  // 获取网格的行数
  13.         n = grid[0].size(); // 获取网格的列数
  14.         // 遍历网格中的每一个格子
  15.         for(int i = 0; i < m; i++)
  16.         {
  17.             for(int j = 0; j < n; j++)
  18.             {
  19.                 if(grid[i][j]) // 如果当前格子有黄金
  20.                 {
  21.                     vis[i][j] = true; // 标记当前格子为已访问
  22.                     dfs(grid, i, j, grid[i][j]); // 从当前格子开始深度优先搜索
  23.                     vis[i][j] = false; // 回溯时恢复状态
  24.                 }
  25.             }
  26.         }
  27.         return ret; // 返回找到的最大黄金总量
  28.     }
  29.     // 深度优先搜索函数
  30.     void dfs(vector<vector<int>>& grid, int i, int j, int path)
  31.     {
  32.         ret = max(ret, path); // 更新最大黄金总量
  33.         // 遍历四个方向
  34.         for(int k = 0; k < 4; k++)
  35.         {
  36.             int x = i + dx[k]; // 计算新的行坐标
  37.             int y = j + dy[k]; // 计算新的列坐标
  38.             // 判断新坐标是否合法、是否未访问以及是否有黄金
  39.             if(x >= 0 && y >= 0 && x < m && y < n && !vis[x][y] && grid[x][y])
  40.             {
  41.                 vis[x][y] = true; // 标记新位置为已访问
  42.                 dfs(grid, x, y, grid[x][y] + path); // 递归搜索
  43.                 vis[x][y] = false; // 回溯时恢复状态
  44.             }
  45.         }
  46.     }
  47. };
复制代码
不同的路径|||


思路:回溯+深度优先搜刮 (DFS)


  • 在网格中探求一条路径,要求:
    从起点走到尽头。
    必须经过所有空格,不能遗漏也不能重复。
    使用回溯法遍历网格:
    遍历网格找到起点,并统计需要经过的空格数量。
    从起点出发,递归搜刮四个方向:
    标记当前点已访问。
    如果到达尽头且已访问所有空格,路径计数+1。
    搜刮完成后,规复当前点状态(回溯)。
    返回所有满足条件的路径总数。
  1. class Solution
  2. {
  3.     int m, n, step; // m 和 n 是网格的行列大小,step 是需要经过的格子总数
  4.     bool vis[21][21]; // 标记网格中的格子是否已被访问,避免重复访问
  5.     int dx[4] = {0, 0, -1, 1}; // 表示水平方向的移动(左右)
  6.     int dy[4] = {-1, 1, 0, 0}; // 表示垂直方向的移动(上下)
  7.     int ret; // 记录所有满足条件的路径数
  8. public:
  9.     // 主函数:返回所有满足条件的路径数
  10.     int uniquePathsIII(vector<vector<int>>& grid)
  11.     {
  12.         m = grid.size();  // 获取网格的行数
  13.         n = grid[0].size(); // 获取网格的列数
  14.         int bx = 0, by = 0; // 起点坐标
  15.         // 遍历网格,初始化起点和统计需要经过的格子总数
  16.         for(int i = 0; i < m; i++)
  17.         {
  18.             for(int j = 0; j < n; j++)
  19.             {
  20.                 if(grid[i][j] == 0) step++; // 统计值为 0 的空格
  21.                 else if(grid[i][j] == 1)   // 找到起点
  22.                 {
  23.                     bx = i;
  24.                     by = j;
  25.                 }
  26.             }
  27.         }
  28.         step += 2; // 包括起点和终点在内,总共需要经过的格子数
  29.         // 从起点开始进行 DFS
  30.         vis[bx][by] = true; // 标记起点为已访问
  31.         dfs(grid, bx, by, 1); // 起点已访问,计数为 1
  32.         return ret; // 返回有效路径的数量
  33.     }
  34.     // 深度优先搜索函数
  35.     void dfs(vector<vector<int>>& grid, int i, int j, int count)
  36.     {
  37.         // 如果当前格子是终点(值为 2)
  38.         if(grid[i][j] == 2)
  39.         {
  40.             // 如果路径经过了所有需要访问的格子
  41.             if(count == step)
  42.                 ret++; // 计数器加 1
  43.             return;
  44.         }
  45.         // 遍历当前格子的四个方向
  46.         for(int k = 0; k < 4; k++)
  47.         {
  48.             int x = i + dx[k]; // 新的行坐标
  49.             int y = j + dy[k]; // 新的列坐标
  50.             // 判断新位置是否合法
  51.             if(x >= 0 && y >= 0 && x < m && y < n && grid[x][y] != -1 && !vis[x][y])
  52.             {
  53.                 vis[x][y] = true; // 标记新位置为已访问
  54.                 dfs(grid, x, y, count + 1); // 递归搜索下一步
  55.                 vis[x][y] = false; // 回溯时恢复状态
  56.             }
  57.         }
  58.     }
  59. };
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

篮之新喜

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表