深度优先搜索

深度优先搜索

定义

  • 深度优先搜索, Deep First Search, 简称 dfs , 因其「一条路走到黑」的特性, 得此名字;
  • 实际上 dfs 类似 暴力枚举, 列出所有可能路径, 尝试所有的路, 直到找出答案;
  • 常用实现手段为 递归调用, 也有非递归方法, 但是递归更加方便

    实现

  1. 模板
  • 和递归函数一样, 需要有递归结束的边界条件, 以及不满足条件时需要执行的操作
  • 需要有访问标志, 一般来说是用一个bool 数组

应用

探索连通路径

  • 举例: 走迷宫 可行解 与 最优解 ``` / 可行解版本 / char map[10][10]; bool vis[10][10]; //dfs 必备访问标志数组 bool dfs(int x, int y) { //首先写边界条件 if (map[x][y] == 终点) {
    1. return true;
    } //否则执行暴力搜索操作 并再次调用 vis[x][y] = true; //更新访问 int tx = x + 新操作; int ty = y + 新操作; //判断条件 比如 是否在地图内 是否走过这一步 是否能走这里 if (in(tx, ty) && map[tx][ty] != 墙壁 && !vis[tx][ty] ) {
    1. if (dfs(tx, ty)) {
    2. return true;
    3. }
    } return false; }
  1. ```
  2. /* 最优解版本 引入走了多少步这个参数 */
  3. char map[10][10];
  4. bool vis[10][10]; //dfs 必备访问标志数组
  5. bool dfs(int x, int y, int step) {
  6. //首先写边界条件
  7. if (map[x][y] == 终点) {
  8. return true;
  9. }
  10. //否则执行暴力搜索操作 并再次调用
  11. vis[x][y] = true; //更新访问
  12. int tx = x + 新操作;
  13. int ty = y + 新操作;
  14. //判断条件 比如 是否在地图内 是否走过这一步 是否能走这里
  15. if (in(tx, ty) && map[tx][ty] != 墙壁 && !vis[tx][ty] ) {
  16. //如果能走 则步数加1
  17. if (dfs(tx, ty, step + 1)) {
  18. return true;
  19. }
  20. }
  21. //值得注意的一个操作 是否要取消访问标志
  22. //取决于 求可行解 还是 最优解
  23. //最优解需要这一步
  24. //因为虽然走不通这个点 但这个点也有可能时最优解的一部分
  25. vis[x][y] = false;
  26. return false;
  27. }

求连通分量个数

  1. 性质: 一个图如果完全连通, 则只需要一次 dfs 就能遍历所有点, 即有一个连通分量, 否则连通分量的个数为 dfs 的调用个数
  2. 举例: 踏青 求草丛个数 “#”为草丛 ``` //输出 5 6 .#…. ..#… ..#..# …##. .#….

//输出 5

include

using namespace std; char map[105][105]; bool v[105][105]; int d[4][2] = {{0, -1}, {-1, 0}, {0, 1}, {1, 0}}; int ans = 0; int n, m; bool in(int x, int y) { return 0 <= x && x < n && 0 <= y && y < m; } void dfs(int x, int y) { int tx, ty; v[x][y] = 1; for (int i = 0; i < 4; i++) { tx = x + d[i][0]; ty = y + d[i][1]; if (in(tx, ty) && map[tx][ty] == ‘#’ && !v[tx][ty]) { dfs(tx, ty); //满足条件则再次调用 继续搜索 } } } int main() { cin >> n >> m; for (int i = 0; i < n; i++) { scanf(“%s”, map[i]); } for (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) { //如果是连通图的话 调用一次 dfs 所有点都被访问过 if (map[i][j] == ‘#’ && !v[i][j]) { dfs(i, j); ans++; //每调用一次记录一次 } } } cout << ans << endl;
return 0; }

```