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

前言

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

2月26日,day08 打卡

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

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

DFS与BFS

DFS

搜索与图论(一)——DFS、BFS、树与图的遍历_第1张图片

动图

只有无路可走了才会回溯
搜索与图论(一)——DFS、BFS、树与图的遍历_第2张图片
DFS中最重要的两个概念

回溯、剪枝

AcWing 842. 排列数字

输出全排列
搜索与图论(一)——DFS、BFS、树与图的遍历_第3张图片

如何用DFS(俗称暴搜)来做
搜索与图论(一)——DFS、BFS、树与图的遍历_第4张图片
一步一步枚举
搜索与图论(一)——DFS、BFS、树与图的遍历_第5张图片
结束一条路径后回溯

动图
搜索与图论(一)——DFS、BFS、树与图的遍历_第6张图片

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

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

搜索与图论(一)——DFS、BFS、树与图的遍历_第7张图片

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

#include 

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:全排列

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

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

剪枝优化

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

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

搜索与图论(一)——DFS、BFS、树与图的遍历_第8张图片

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

搜索与图论(一)——DFS、BFS、树与图的遍历_第9张图片

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

#include 

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个选择,挨个枚举每个格子
搜索与图论(一)——DFS、BFS、树与图的遍历_第10张图片
出界了就到下一个格子
搜索与图论(一)——DFS、BFS、树与图的遍历_第11张图片

#include 

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 == n) 

你可能感兴趣的:(#,acwing算法基础课,深度优先,算法,图论)