保研考研机试攻略:第六章——搜索(2)

🍦🍦🍦今天我们继续来学习搜索的后半部分:深度优先搜索(DFS)、搜索剪枝技巧、终极骗分技巧等内容。我们一起加油鸭ヾ(≧▽≦*)o ~ go go go~

目录

[🧊🧊🧊6.4 深度优先搜索(DFS)](#🧊🧊🧊6.4 深度优先搜索(DFS))

[🥥例题:DreamJudge 1563](#🥥例题:DreamJudge 1563)

[🥥例题:DreamJudge 1564](#🥥例题:DreamJudge 1564)

[🧊🧊🧊6.5 搜索剪枝技巧](#🧊🧊🧊6.5 搜索剪枝技巧)

什么是剪枝?

剪枝的原则?

1.可行性剪枝

2.最优性剪枝

3.记忆化搜索

4.搜索顺序剪枝

[🧊🧊🧊6.6 终极骗分技巧](#🧊🧊🧊6.6 终极骗分技巧)

1、利用预处理的思想

2、暴力剪枝

3、利用贪心的思想

4、利用概率论的思想


🧊🧊🧊6.4 深度优先搜索(DFS)

深度优先搜索的实现形式就是递归,如果把递归的流程画出来,那么我们可以得到一棵递归树,其实就是一个多叉树。

BFS 和 DFS 有什么区别呢?

对于一棵二叉树,BFS 就是二叉树的层次遍历,一层一层的扩展下去;DFS 就是二叉树的中序遍历,一条路走到底,然后回溯走第二条,直到所有路都走完。

大家需要注意的是,DFS一般情况下效率不如BFS,比如求DFS中的迷宫的最短路径使用DFS就会超时。

🥥例题:DreamJudge 1563

直接用DFS模板,但是数据量大的时候会超时,如下代码:(后边会有剪枝部分的学习)

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
const int maxn = 100 + 5;
char mpt[maxn][maxn];
int vis[maxn][maxn];
int dir[4][2] = {1, 0, 0, -1, -1, 0, 0, 1};//右下左上
int ans;
//使用深度优先搜索求解
void dfs(int x, int y, int step) {
    if (step >= ans) return;//一个小剪枝,当步数超过答案就不用继续
    if (mpt[x][y] == 'E') {//找到终点
        ans = min(ans, step);
        return;
    }
    for (int i = 0; i < 4; i++) {//上下左右四个方向
        int nx = x + dir[i][0];
        int ny = y + dir[i][1];
        if ((mpt[nx][ny] == '*' || mpt[nx][ny] == 'E')&&vis[nx][ny] == 0) {
            vis[nx][ny] = 1;
            dfs(nx, ny, step+1);
            vis[nx][ny] = 0;
        }
    }
}
int main() {
    int h, w;
    while (scanf("%d%d", &h, &w) != EOF) {
        if (h == 0 && w == 0) break;
        int sx = 0, sy = 0;
        memset(mpt, 0, sizeof(mpt));
        memset(vis, 0, sizeof(vis));
        for (int i = 1; i <= h; i++) {
            scanf("%s", mpt[i] + 1);
            for (int j = 1; j <= w; j++) {
                if (mpt[i][j] == 'S') {
                    sx = i, sy = j;//记录起点坐标
                }
            }
        }
        ans = 99999999;
        dfs(sx, sy, 0);
        printf("%d\n", ans);
    }
    return 0;
}

🥥例题:DreamJudge 1564

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
const int maxn = 100 + 5;
char mpt[maxn][maxn];
int vis[maxn][maxn];
int dir[8][2] = {1,0,0,-1,-1,0,0,1,1,1,1,-1,-1,1,-1,-1};
//使用深度优先搜索求解
int dfs(int x, int y) {
    vis[x][y] = 1;
    for (int i = 0; i < 8; i++) {//8 个方向
        int nx = x + dir[i][0];
        int ny = y + dir[i][1];
        if (mpt[nx][ny] == '@' && vis[nx][ny] == 0) {
            dfs(nx, ny);
        }
    }
}
int main() {
    int h, w;
    while (scanf("%d%d", &h, &w) != EOF) {
        if (h == 0 && w == 0) break;
        int sx = 0, sy = 0;
        memset(mpt, 0, sizeof(mpt));
        memset(vis, 0, sizeof(vis));
        for (int i = 1; i <= h; i++) {
            scanf("%s", mpt[i] + 1);
        }
        int ans = 0;
        for (int i = 1; i <= h; i++) {
            for (int j = 1; j <= w; j++) {
                if (vis[i][j] == 0 && mpt[i][j] == '@') {
                    ans++;
                    dfs(i, j);
                }
            }
        }
        printf("%d\n", ans);
    }
    return 0;
}

🧊🧊🧊6.5 搜索剪枝技巧

什么是剪枝?

常用的搜索有 DFS 和 BFS。BFS 的剪枝通常就是判重,因为一般 BFS 寻找的是步数最少,重复的话必定不会在之前的情况产生最优解。深搜,它的进程近似一颗树(通常叫 DFS 树)。

而剪枝就是一种生动的比喻:把不会产生答案的,或不必要的枝条"剪掉"

剪枝的关键就在于剪枝的判断:什么枝该剪,什么枝不该剪,在什么地方减。

剪枝的原则?

正确性,准确性,高效性。

常用的剪枝有:可行性剪枝、最优性剪枝、记忆化搜索、搜索顺序剪枝。

1.可行性剪枝

如果当前条件不合法就不再继续搜索,直接 return。这是非常好理解的剪枝,搜索初学者都能轻松地掌握,而且也很好想。一般的搜索都会加上。

cpp 复制代码
dfs(int x)
{
    if(x>n)return;
    if(!check1(x))return;
    .... 
    return;
}
2.最优性剪枝

如果当前条件所创造出的答案必定比之前的答案大,那么剩下的搜索就毫无必要,甚至可以剪掉。我们利用某个函数估计出此时条件下答案的'下界',将它与已经推出的答案相比,如果不比当前答案小,就可以剪掉。

cpp 复制代码
long long ans=987474477434487ll;
... Dfs(int x,...)
{
    if(x... && ...){ans=....;return ...;}
    if(check2(x)>=ans)return ...; //最优性剪枝
    for(int i=1;...;++i)
    {
        vis[...]=1;
        dfs(...);
        vis[...]=0;
    }
}
//一般实现:在搜索取和最大值时,如果后面的全部取最大仍然不比当前答案大就可以返回。
//在搜和最小时同理,可以预处理后缀最大/最小和进行快速查询。
3.记忆化搜索

记忆化搜索其实很像动态规划(DP)。

它的关键是:如果对于相同情况下必定答案相同,就可以把这个情况的答案值存储下来,以后再次搜索到这种情况时就可以直接调用

还有就是不能搜出环来,不能互相依赖。

cpp 复制代码
long long ans=987474477434487ll;
... Dfs(int x,...)
{
    if(x... && ...){ans=....;return ...;}
    if(vis[x]!=0) return f[x];vis[x]=1;
    for(int i=1;...;++i)
    {
        vis[...]=1;
        dfs(...);
        vis[...]=0;
        f[x]=...;
    }
}
4.搜索顺序剪枝

在一些迷宫题,网格题,或者其他搜索中可以贪心的题,搜索顺序显得十分重要。我们经常听见有人说:"从左边搜会 T,从右边搜就 A 了"之类的话。其实在迷宫、网格类的题目中,以左上->右下为例,右下左上就明显比左上右下优秀。

在一些推断搜索题中,从已知信息最多的地方开始搜索显然更加优秀

在一些题中,先搜某个值大的,再搜某个值小的(比如树的度数,产生答案的预计(A*),速度明显会比乱搜更快。

🧊🧊🧊6.6 终极骗分技巧

有的时候题目的正解并不是搜索,由于数据范围大等原因,我们搜索不管怎么剪枝都会超时。

当你想不到正解,只会搜索的时候,千万不要气馁,我们来学习骗分技巧:

1、利用预处理的思想

这是一种中规中矩的处理方式,先将搜索过程中可能会用到的值预先处理出来,本质是用空间换时间。例如 A*(启发式搜索)就是用的这个思想,这样可以大大节约搜索时的时间开销,也能保证答案的正确性。

2、暴力剪枝

这种方法就好像一棵树上结了一个果子,你不再是每一个树枝都去寻找,而是直接砍掉一些树枝不要,然后再在剩下的树上寻找,很明显这种方法不能保证一定正确,但是却可以大大减少时间的消耗,也可以变成递归到多少层就不继续往下递归了,不管是否后面有没有果子。

3、利用贪心的思想

贪心是一种很简单的思想,虽然贪心不一定是正确,但是却有可能正确。

4、利用概率论的思想

我们都知道现在图像识别已经进入了大家的生活,但是识别不是都能 100%的准确,现在看各个厂家的产品都是说识别率能达到百分之多少,而不是 100%。那么我们可以根据对应的题目去解析几个特征值,然后设置一下概率来触发搜索的条件。

后面三种的使用效果要看各人的天赋,毕竟是绝招嘛,一般情况下不会使用的。(本质上都是利用了考研的机试题目一般都不会特别强的弱点。)

创作不易,点个赞吧~点赞收藏不迷路,感兴趣的宝子们欢迎关注该专栏~

勤奋努力的宝子们,学习辛苦了!宝子们可以收藏起来慢慢学哦~🌷🌷🌷休息下,我们下部分再见👋( •̀ ω •́ )✧~

相关推荐
爱吃喵的鲤鱼8 分钟前
linux进程的状态之环境变量
linux·运维·服务器·开发语言·c++
懒惰才能让科技进步30 分钟前
从零学习大模型(十二)-----基于梯度的重要性剪枝(Gradient-based Pruning)
人工智能·深度学习·学习·算法·chatgpt·transformer·剪枝
7年老菜鸡35 分钟前
策略模式(C++)三分钟读懂
c++·qt·策略模式
Ni-Guvara44 分钟前
函数对象笔记
c++·算法
似霰1 小时前
安卓智能指针sp、wp、RefBase浅析
android·c++·binder
芊寻(嵌入式)1 小时前
C转C++学习笔记--基础知识摘录总结
开发语言·c++·笔记·学习
獨枭1 小时前
C++ 项目中使用 .dll 和 .def 文件的操作指南
c++
霁月风1 小时前
设计模式——观察者模式
c++·观察者模式·设计模式
橘色的喵1 小时前
C++编程:避免因编译优化引发的多线程死锁问题
c++·多线程·memory·死锁·内存屏障·内存栅栏·memory barrier
泉崎1 小时前
11.7比赛总结
数据结构·算法