#c

DFS走迷宫类问题

深度优先搜索在走迷宫类问题中的应用

#c
@鞠大龙
魅客科创中心

CONTENTS

目录

1. 问题概述

1.1 问题概述

  • 走迷宫类问题的特点
    • 在二维或多维空间中搜索路径
    • 需要处理方向移动和状态转移
    • 可能存在多种约束条件
    • 通常需要记录和回溯
  • 常见变种
    • 基础迷宫寻路
    • 最短路径查找
    • 全路径统计
    • 特殊条件限制
  • 核心要素
    • 状态表示
    • 移动规则
    • 约束条件
    • 目标判定
返回目录 信息学 DFS走迷宫类问题 魅客科创中心

1.2 应用场景

  • 迷宫探索
    • 寻找出口
    • 收集物品
    • 避开障碍
  • 棋盘问题
    • 马走日
    • N皇后
    • 骑士巡游
  • 字符矩阵
    • 单词搜索
    • 字母连接
    • 图案匹配
  • 路径规划
    • 机器人导航
    • 游戏寻路
    • 资源收集
返回目录 信息学 DFS走迷宫类问题 魅客科创中心

2. 基本类型

2.1 基础迷宫问题

  • 特点
    • 二维网格结构
    • 有明确的起点和终点
    • 存在障碍物
    • 四个或八个移动方向
  • 变种
    • 最短路径
    • 所有可行路径
    • 特定路径要求
    • 收集物品
// 基础迷宫DFS模板
void mazeDFS(vector<vector<int>>& maze, 
            int x, int y, 
            vector<vector<bool>>& visited) {
    // 边界检查
    if (x < 0 || x >= maze.size() || 
        y < 0 || y >= maze[0].size()) return;
    
    // 障碍物或已访问检查
    if (maze[x][y] == 1 || visited[x][y]) return;
    
    // 标记当前位置
    visited[x][y] = true;
    
    // 四个方向DFS
    mazeDFS(maze, x+1, y, visited);  // 下
    mazeDFS(maze, x-1, y, visited);  // 上
    mazeDFS(maze, x, y+1, visited);  // 右
    mazeDFS(maze, x, y-1, visited);  // 左
    
    // 回溯(如果需要)
    // visited[x][y] = false;
}
返回目录 信息学 DFS走迷宫类问题 魅客科创中心

2.2 字母矩阵问题

  • 特点
    • 字符组成的二维矩阵
    • 需要寻找特定字符序列
    • 可以多方向移动
    • 字符可重复/不可重复使用
  • 变种
    • 单词搜索
    • 字符串构建
    • 图案匹配
    • 路径字符串要求
// 字母矩阵DFS模板
bool wordDFS(vector<vector<char>>& board, 
            string& word, int index,
            int x, int y, 
            vector<vector<bool>>& visited) {
    // 找到完整单词
    if (index == word.length()) return true;
    
    // 边界和字符检查
    if (x < 0 || x >= board.size() || 
        y < 0 || y >= board[0].size() ||
        board[x][y] != word[index] ||
        visited[x][y]) return false;
    
    // 标记当前位置
    visited[x][y] = true;
    
    // 四个方向DFS
    bool found = wordDFS(board, word, index+1, x+1, y, visited) ||
                wordDFS(board, word, index+1, x-1, y, visited) ||
                wordDFS(board, word, index+1, x, y+1, visited) ||
                wordDFS(board, word, index+1, x, y-1, visited);
    
    // 回溯
    visited[x][y] = false;
    
    return found;
}
返回目录 信息学 DFS走迷宫类问题 魅客科创中心

2.3 骑士巡游问题

  • 特点
    • 特定的移动规则
    • 需要遍历所有格子
    • 每个格子只能访问一次
    • 可能需要回到起点
  • 变种
    • 开放巡游
    • 闭合巡游
    • 最短路径
    • 特定访问顺序
// 骑士巡游DFS模板
bool knightTourDFS(vector<vector<int>>& board,
                   int x, int y, int move) {
    // 完成巡游
    if (move == board.size() * board[0].size()) 
        return true;
    
    // 八个可能的移动方向
    int dx[] = {2,1,-1,-2,-2,-1,1,2};
    int dy[] = {1,2,2,1,-1,-2,-2,-1};
    
    // 尝试每个方向
    for (int i = 0; i < 8; i++) {
        int nextX = x + dx[i];
        int nextY = y + dy[i];
        
        if (isValid(board, nextX, nextY) && 
            board[nextX][nextY] == -1) {
            board[nextX][nextY] = move;
            
            if (knightTourDFS(board, nextX, nextY, move + 1))
                return true;
                
            // 回溯
            board[nextX][nextY] = -1;
        }
    }
    
    return false;
}
返回目录 信息学 DFS走迷宫类问题 魅客科创中心

2.4 特殊约束问题

  • 特点
    • 复杂的移动规则
    • 多重约束条件
    • 状态依赖关系
    • 动态环境变化
  • 变种
    • 带权路径
    • 条件触发
    • 资源限制
    • 时序约束
// 特殊约束DFS模板
bool constrainedDFS(vector<vector<int>>& grid,
                   int x, int y,
                   vector<vector<bool>>& visited,
                   State& state) {
    // 检查约束条件
    if (!checkConstraints(state)) return false;
    
    // 到达目标
    if (isGoal(x, y, state)) return true;
    
    // 标记当前状态
    visited[x][y] = true;
    State oldState = state;
    
    // 遍历可能的移动
    for (auto& move : getPossibleMoves(x, y, state)) {
        // 更新状态
        updateState(state, move);
        
        if (constrainedDFS(grid, move.x, move.y, 
                          visited, state))
            return true;
            
        // 回溯状态
        state = oldState;
    }
    
    visited[x][y] = false;
    return false;
}
返回目录 信息学 DFS走迷宫类问题 魅客科创中心

3. 设计步骤

3. 设计步骤

  1. 分析问题特点

    • 确定问题类型(迷宫、字母矩阵、骑士巡游等)
    • 识别移动规则和约束条件
    • 明确目标状态
  2. 设计状态表示

    • 定义位置信息(坐标、方向)
    • 确定额外状态信息(步数、路径、资源等)
    • 设计状态转移规则
  1. 实现核心函数

    • 编写DFS主函数
    • 实现状态检查和更新
    • 处理边界情况和特殊条件
  2. 优化和改进

    • 添加剪枝条件
    • 优化状态表示
    • 改进搜索策略
返回目录 信息学 DFS走迷宫类问题 魅客科创中心

4. 模板代码

4.1 基础迷宫模板

class MazeSolver {
private:
    // 方向数组
    const int dx[4] = {-1, 1, 0, 0};  // 上下左右
    const int dy[4] = {0, 0, -1, 1};
    int rows, cols;
    vector<vector<int>> maze;
    vector<vector<bool>> visited;
    vector<pair<int,int>> path;
    
    bool isValid(int x, int y) {
        return x >= 0 && x < rows && y >= 0 && y < cols && 
               maze[x][y] == 0 && !visited[x][y];
    }
    
    bool dfs(int x, int y, int endX, int endY) {
        // 到达终点
        if (x == endX && y == endY) {
            path.push_back({x, y});
            return true;
        }
        
        // 标记当前位置
        visited[x][y] = true;
        path.push_back({x, y});
        
        // 尝试四个方向
        for (int i = 0; i < 4; i++) {
            int nextX = x + dx[i];
            int nextY = y + dy[i];
            
            if (isValid(nextX, nextY)) {
                if (dfs(nextX, nextY, endX, endY))
                    return true;
            }
        }
        
        // 回溯
        path.pop_back();
        return false;
    }
    
public:
    MazeSolver(vector<vector<int>>& m) : maze(m) {
        rows = maze.size();
        cols = maze[0].size();
        visited = vector<vector<bool>>(rows, vector<bool>(cols, false));
    }
    
    vector<pair<int,int>> findPath(int startX, int startY, 
                                  int endX, int endY) {
        path.clear();
        if (dfs(startX, startY, endX, endY))
            return path;
        return vector<pair<int,int>>();
    }
};
返回目录 信息学 DFS走迷宫类问题 魅客科创中心

4.2 字母矩阵模板

class WordSearcher {
private:
    const int dx[8] = {-1,-1,-1,0,0,1,1,1};  // 8个方向
    const int dy[8] = {-1,0,1,-1,1,-1,0,1};
    int rows, cols;
    vector<vector<char>> board;
    vector<vector<bool>> visited;
    
    bool isValid(int x, int y) {
        return x >= 0 && x < rows && y >= 0 && y < cols && !visited[x][y];
    }
    
    bool dfs(int x, int y, const string& word, int index) {
        // 找到完整单词
        if (index == word.length()) return true;
        
        // 当前字符不匹配
        if (board[x][y] != word[index]) return false;
        
        // 标记当前位置
        visited[x][y] = true;
        
        // 尝试八个方向
        for (int i = 0; i < 8; i++) {
            int nextX = x + dx[i];
            int nextY = y + dy[i];
            
            if (isValid(nextX, nextY)) {
                if (dfs(nextX, nextY, word, index + 1))
                    return true;
            }
        }
        
        // 回溯
        visited[x][y] = false;
        return false;
    }
    
public:
    WordSearcher(vector<vector<char>>& b) : board(b) {
        rows = board.size();
        cols = board[0].size();
        visited = vector<vector<bool>>(rows, vector<bool>(cols, false));
    }
    
    bool exist(string word) {
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                if (board[i][j] == word[0]) {
                    if (dfs(i, j, word, 0))
                        return true;
                }
            }
        }
        return false;
    }
};
返回目录 信息学 DFS走迷宫类问题 魅客科创中心

5. 优化技巧

5. 优化技巧

  • 方向优化
    • 使用方向数组简化代码
    • 优先搜索更有希望的方向
    • 根据问题特点选择合适的方向顺序
  • 状态优化
    • 使用位运算表示状态
    • 压缩状态信息
    • 避免重复状态
  • 剪枝优化
    • 可行性剪枝
    • 最优性剪枝
    • 对称性剪枝
  • 记忆化优化
    • 存储中间结果
    • 避免重复计算
    • 使用合适的数据结构
返回目录 信息学 DFS走迷宫类问题 魅客科创中心

6. 经典例题

6.1 迷宫寻路

问题描述:在N×M的迷宫中找到从起点到终点的路径,0表示通道,1表示墙壁。

思路分析

  1. 使用DFS遍历所有可能路径
  2. 记录已访问位置避免循环
  3. 维护当前路径信息
  4. 找到任意一条可行路径即可
返回目录 信息学 DFS走迷宫类问题 魅客科创中心

6.1.1 迷宫寻路代码实现

class MazeSolver {
private:
    vector<vector<int>> maze;
    vector<vector<bool>> visited;
    vector<pair<int,int>> path;
    int rows, cols;
    const int dx[4] = {-1, 1, 0, 0};
    const int dy[4] = {0, 0, -1, 1};
    
    bool isValid(int x, int y) {
        return x >= 0 && x < rows && y >= 0 && y < cols && 
               maze[x][y] == 0 && !visited[x][y];
    }
    
    bool dfs(int x, int y, int endX, int endY) {
        if (x == endX && y == endY) {
            path.push_back({x, y});
            return true;
        }
        
        visited[x][y] = true;
        path.push_back({x, y});
        
        for (int i = 0; i < 4; i++) {
            int nextX = x + dx[i];
            int nextY = y + dy[i];
            
            if (isValid(nextX, nextY)) {
                if (dfs(nextX, nextY, endX, endY))
                    return true;
            }
        }
        
        path.pop_back();
        return false;
    }
    
public:
    MazeSolver(vector<vector<int>>& m) : maze(m) {
        rows = maze.size();
        cols = maze[0].size();
        visited = vector<vector<bool>>(rows, vector<bool>(cols, false));
    }
    
    vector<pair<int,int>> findPath(int startX, int startY, 
                                  int endX, int endY) {
        path.clear();
        if (dfs(startX, startY, endX, endY))
            return path;
        return vector<pair<int,int>>();
    }
};
返回目录 信息学 DFS走迷宫类问题 魅客科创中心

6.2 单词搜索

问题描述:在字母矩阵中搜索是否存在给定的单词,可以上下左右四个方向移动。

思路分析

  1. 从每个可能的起点开始DFS
  2. 按字母顺序匹配单词
  3. 标记已访问位置
  4. 找到完整单词则返回true
返回目录 信息学 DFS走迷宫类问题 魅客科创中心

6.2.1 单词搜索代码实现

class WordSearcher {
private:
    vector<vector<char>> board;
    vector<vector<bool>> visited;
    int rows, cols;
    const int dx[4] = {-1, 1, 0, 0};
    const int dy[4] = {0, 0, -1, 1};
    
    bool isValid(int x, int y) {
        return x >= 0 && x < rows && y >= 0 && y < cols && !visited[x][y];
    }
    
    bool dfs(int x, int y, const string& word, int index) {
        if (index == word.length()) return true;
        if (board[x][y] != word[index]) return false;
        
        visited[x][y] = true;
        
        for (int i = 0; i < 4; i++) {
            int nextX = x + dx[i];
            int nextY = y + dy[i];
            
            if (isValid(nextX, nextY)) {
                if (dfs(nextX, nextY, word, index + 1))
                    return true;
            }
        }
        
        visited[x][y] = false;
        return false;
    }
    
public:
    WordSearcher(vector<vector<char>>& b) : board(b) {
        rows = board.size();
        cols = board[0].size();
        visited = vector<vector<bool>>(rows, vector<bool>(cols, false));
    }
    
    bool exist(string word) {
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                if (board[i][j] == word[0]) {
                    if (dfs(i, j, word, 0))
                        return true;
                }
            }
        }
        return false;
    }
};
返回目录 信息学 DFS走迷宫类问题 魅客科创中心

6.3 骑士巡游

问题描述:在N×N的棋盘上,骑士从给定起点出发,访问每个格子恰好一次。

思路分析

  1. 使用DFS尝试所有可能的移动序列
  2. 记录已访问的格子
  3. 使用Warnsdorff启发式优化
  4. 当访问所有格子时完成巡游
返回目录 信息学 DFS走迷宫类问题 魅客科创中心

6.3.1 骑士巡游代码实现

class KnightTour {
private:
    vector<vector<int>> board;
    int size;
    const int dx[8] = {2,1,-1,-2,-2,-1,1,2};
    const int dy[8] = {1,2,2,1,-1,-2,-2,-1};
    
    bool isValid(int x, int y) {
        return x >= 0 && x < size && y >= 0 && y < size && board[x][y] == -1;
    }
    
    // Warnsdorff启发式:计算下一步可用移动数
    int getDegree(int x, int y) {
        int count = 0;
        for (int i = 0; i < 8; i++) {
            int nextX = x + dx[i];
            int nextY = y + dy[i];
            if (isValid(nextX, nextY)) count++;
        }
        return count;
    }
    
    bool dfs(int x, int y, int move) {
        if (move == size * size) return true;
        
        vector<pair<int,int>> nextMoves;
        // 收集所有可能的下一步移动
        for (int i = 0; i < 8; i++) {
            int nextX = x + dx[i];
            int nextY = y + dy[i];
            if (isValid(nextX, nextY)) {
                int degree = getDegree(nextX, nextY);
                nextMoves.push_back({degree, i});
            }
        }
        
        // 按Warnsdorff规则排序
        sort(nextMoves.begin(), nextMoves.end());
        
        for (auto& p : nextMoves) {
            int i = p.second;
            int nextX = x + dx[i];
            int nextY = y + dy[i];
            
            board[nextX][nextY] = move;
            if (dfs(nextX, nextY, move + 1))
                return true;
            board[nextX][nextY] = -1;
        }
        
        return false;
    }
    
public:
    KnightTour(int n) : size(n) {
        board = vector<vector<int>>(n, vector<int>(n, -1));
    }
    
    bool findTour(int startX, int startY) {
        board[startX][startY] = 0;
        return dfs(startX, startY, 1);
    }
    
    void printTour() {
        for (int i = 0; i < size; i++) {
            for (int j = 0; j < size; j++) {
                cout << board[i][j] << "\t";
            }
            cout << endl;
        }
    }
};
返回目录 信息学 DFS走迷宫类问题 魅客科创中心

7. 总结

7. 总结

  • DFS是解决走迷宫类问题的有效方法
  • 理解不同类型问题的特点和解决策略
  • 掌握优化技巧,提高搜索效率
  • 灵活运用模板代码,解决实际问题
  • 通过练习题目加深理解和应用能力
返回目录 信息学 DFS走迷宫类问题 魅客科创中心

8. 练习题目

8.1 初级练习题

  1. [HDU 1312] Red and Black

    • 难度:★★☆☆☆
    • 描述:在黑白瓷砖组成的房间中,从起点出发,计算可以到达的白色瓷砖数量
    • 思路:基础DFS,标记已访问位置,统计可达白色瓷砖数
  2. [POJ 3009] Curling 2.0

    • 难度:★★☆☆☆
    • 描述:在冰面上投掷石头,石头会一直滑行直到碰到障碍物,求到达目标的最少投掷次数
    • 思路:DFS + 状态记录,注意处理石头滑行过程
  3. [CSP-J 2019] 迷宫

    • 难度:★★☆☆☆
    • 描述:在N×M的迷宫中找到从起点到终点的路径,路径上的数字之和最小
    • 思路:DFS + 路径记录,需要维护当前路径和最优路径
返回目录 信息学 DFS走迷宫类问题 魅客科创中心

8.2 中级练习题

  1. [POJ 1724] ROADS

    • 难度:★★★☆☆
    • 描述:在带权图中寻找从起点到终点的路径,路径长度不超过给定值,且花费最小
    • 思路:DFS + 剪枝,需要同时考虑路径长度和花费
  2. [USACO 2008] Word Game

    • 难度:★★★☆☆
    • 描述:在字母矩阵中寻找所有可能的单词,单词可以沿八个方向延伸
    • 思路:DFS + 字典树优化,需要处理多方向搜索
  3. [CSP-S 2019] 骑士

    • 难度:★★★☆☆
    • 描述:在N×M的棋盘上,按照马的走法遍历所有格子,求所有可能的遍历序列
    • 思路:DFS + 回溯,需要处理马的移动规则和全局遍历
返回目录 信息学 DFS走迷宫类问题 魅客科创中心

8.3 高级练习题

  1. [POJ 2488] A Knight's Journey

    • 难度:★★★★☆
    • 描述:在给定大小的棋盘上,找出一条访问每个格子恰好一次的马路径
    • 思路:DFS + 回溯 + 优化,需要考虑字典序最小的解
  2. [USACO 2012] Cow Run

    • 难度:★★★★☆
    • 描述:在二维网格中,寻找一条路径访问所有奶牛,且路径长度最短
    • 思路:DFS + 状态压缩 + 剪枝,需要处理复杂的状态转移
  3. [CSP-S 2020] 子序列

    • 难度:★★★★☆
    • 描述:在网格中寻找满足特定条件的路径,路径上的数字构成给定序列
    • 思路:DFS + 动态规划,需要处理复杂的路径约束
返回目录 信息学 DFS走迷宫类问题 魅客科创中心

8.4 综合应用题

  1. [POJ 3411] Paid Roads

    • 难度:★★★★★
    • 描述:在带条件的图中寻找最优路径,每条边的使用成本取决于之前的路径选择
    • 思路:DFS + 状态记录 + 剪枝,需要处理复杂的状态依赖关系
  2. [USACO 2015] Trapped in the Haybales

    • 难度:★★★★★
    • 描述:在迷宫中寻找路径,但某些位置会触发特殊事件改变迷宫状态
    • 思路:DFS + 状态空间搜索,需要处理动态变化的迷宫环境
  3. [CSP-S 2021] 生物群落

    • 难度:★★★★★
    • 描述:在复杂的生态系统网格中,寻找满足多个约束条件的生物分布方案
    • 思路:DFS + 约束满足 + 优化,需要处理多重约束和状态转移
返回目录 信息学 DFS走迷宫类问题 魅客科创中心

8.5 练习建议

  1. 循序渐进

    • 从基础迷宫问题开始
    • 逐步尝试更复杂的问题
    • 注意总结每类问题的特点
  2. 实现要点

    • 正确处理边界条件
    • 注意状态的保存和恢复
    • 合理使用优化技巧
  3. 调试技巧

    • 使用小规模测试数据
    • 打印中间状态进行验证
    • 注意特殊情况的处理
  4. 提高建议

    • 总结每类问题的解题模板
    • 练习不同类型的变体
    • 尝试优化解决方案
返回目录 信息学 DFS走迷宫类问题 魅客科创中心
DFS走迷宫类问题

#c

演讲者备注不会在演示模式中显示给观众,仅供演讲者参考。 每个关键页面都添加了备注,帮助演讲者更好地讲解内容。

封面页: - 欢迎学生,简要介绍本次课程的主题和目标 - 强调DFS在走迷宫类问题中的重要应用 - 本讲义将系统讲解DFS在走迷宫、letters、马走日等问题中的应用

目录页: - 简要介绍讲义结构 - DFS在走迷宫类问题中的应用、基本类型、设计步骤、优化技巧、经典例题

转场页: - 引入DFS在走迷宫类问题中的应用

问题概述: - DFS在走迷宫类问题中的应用场景 - 基本概念和术语

转场页: - DFS在走迷宫类问题中的基本类型

基础迷宫问题: - 定义和特点 - 常见的基础迷宫问题例子 - 基础迷宫问题的实现方式

字母矩阵问题: - 定义和特点 - 常见的字母矩阵问题例子 - 字母矩阵问题的实现方式

骑士巡游问题: - 定义和特点 - 常见的骑士巡游问题例子 - 骑士巡游问题的实现方式

特殊约束问题: - 定义和特点 - 常见的特殊约束问题例子 - 特殊约束问题的实现方式

转场页: - DFS走迷宫类问题的设计流程

转场页: - DFS走迷宫类问题的代码模板

转场页: - DFS走迷宫类问题的优化方法

转场页: - DFS走迷宫类问题的经典例题

转场页: - 总结DFS在走迷宫类问题中的应用

转场页: - DFS走迷宫类问题练习题

练习题目部分结束,这些题目涵盖了DFS算法的各个难度和应用场景。 鼓励学生尝试解决这些问题,加深对DFS思想的理解。