搜索与图论(一)——DFS、BFS、树与图的遍历

这篇博客介绍了搜索与图论的基础知识,包括DFS和BFS两种搜索算法,通过AcWing上的题目实例讲解了它们的应用,如排列数字、n-皇后问题、走迷宫和八数码。此外,还探讨了树与图的深度优先遍历、宽度优先遍历以及拓扑排序,并提供了相应的模板和解题思路。
摘要由CSDN通过智能技术生成

前言

重学算法第8天,希望能坚持打卡不间断,每天早起学习算法
明天再来!肝就完了

2月26日,day08 打卡

今日已学完y总的
算法基础课-3.1,3.2
第三章 搜索与图论(一)+ Week4——习题课

共7题,知识点如下
DFS:排列数字、n-皇后问题。
BFS:走迷宫、八数码。
树与图的深度优先遍历:树的重心
树与图的广度优先遍历:图中点的层次
拓扑排序:有向图的拓扑序列

DFS与BFS

DFS

在这里插入图片描述

动图

只有无路可走了才会回溯
在这里插入图片描述
DFS中最重要的两个概念

回溯、剪枝

AcWing 842. 排列数字

输出全排列
在这里插入图片描述

如何用DFS(俗称暴搜)来做
在这里插入图片描述
一步一步枚举
在这里插入图片描述
结束一条路径后回溯

动图
在这里插入图片描述

没必要区分DFS和递归
只要形式是这样的,都可以称为递归

每次存的都是一条路径,递归下一步就没了,不用存一棵树,
而且系统会有一个隐藏的栈来维护路径,不需要开额外空间

在这里插入图片描述

回溯的时候一定要注意
每次回来一定要恢复原样,从该点下去的时候是啥样,回来也得是那样
用完的东西一定要放回去

#include <iostream>

using namespace std;

const int N = 10;

int n;
int path[N]; // 用来存走的路
bool st[N]; // 为true表示该数用过了

void dfs(int u) {
   
    if (u == n) {
    // 走到n说明这条路已经走完了
        for (int i = 0; i < n; i++) printf("%d ", path[i]);
        puts(""); // 换行
        return;
    }
    // 还没走到最后一层
    for (int i = 1; i <= n; i++) {
   
        if (!st[i]) {
    // 如果st[i] == false,即该数i没用过
            path[u] = i; // 将i放到当前数上
            st[i] = true; // i被用过了
            dfs(u + 1); // 走到下一层(进到递归就修改,出来了就要及时恢复)
            // 恢复现场
            st[i] = false;
        }
    }
}

int main() {
   
    cin >> n;
    
    dfs(0); // 从第0个位置开始看
    
    return 0;
}

AcWing 843. n-皇后问题

思路1:全排列

与全排列一样搜,每一行只能放一个皇后,枚举每一行,从前往后看皇后应该放到哪个位置

该思路与全排列完全一致,一排一排往下搜,搜完一条路径,折回来继续下一个

剪枝优化

如果走到某一排发现已经不满足性质了,就不往后 走了,直接回溯

将该行删掉,可以看成是将该树枝减掉了

在这里插入图片描述

对角线这么看,最上面的是第一条
正着的和反着的

在这里插入图片描述

两种对角线对应的截距(就是对角线的编号),b=y-x可能为负数,加上n
代码中i是横坐标(x)
u是纵坐标(y)

#include <iostream>

using namespace std;

const int N = 20; // 对角线个数2N-1

int n;
char g[N][N]; // 记录方案
// 每列,正对角线和反对角线,都只能1个
bool col[N], dg[N], udg[N]; 
// i即x坐标,u即y坐标
void dfs(int u) {
   
    if (u == n) {
   
        for (int i = 0; i < n; i++) puts(g[i]);
        puts("");
        return;
    }

    for (int i = 0; i < n; i++) {
   
        if (!col[i] && !dg[u + i] && !udg[n - i + u]) {
    // 列,对角线和反对角线都没放过
            g[i][u] = 'Q';
            col[i] = dg[u + i] = udg[n - i + u] = true; // 已经走过了,置为true
            dfs(u + 1);
            col[i] = dg[u + i] = udg[n - i + u] = false; // 恢复现场
            g[i][u] = '.';
        }
    }
}

int main() {
   
    cin >> n;
    for (int i = 0; i < n; i++) {
   
        for (int j = 0; j < n; j++) {
   
            g[i][j] = '.';
        }
    }
    
    dfs(0);
    
    return 0;
}

DFS没有经典模板,重要的是顺序

思路二:枚举

枚举,更原始的方式

每个格子都有放与不放2个选择,挨个枚举每个格子
在这里插入图片描述
出界了就到下一个格子
在这里插入图片描述

#include <iostream>

using namespace std;

const int N = 20; 

int n;
char g[N][N];
// col 列 (往左右走) x
// row 行 (往上下走) y
bool col[N], row[N], dg[N], udg[N]; 

void dfs(int x, int y, int s) {
   
    if (x == n) x = 0, y++;  // 第一排找完了,到下一排开头去
    if (y == n) {
    // 到最后一排了
        if (s == n) {
   
            if (s ==
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值