基础算法篇(2)(蓝桥杯常考点)—搜索

文章内容概要

本次文章将会讲算法中的搜索。这几个内容在蓝桥杯中非常的常考,建议大家认真阅读。

下期将会为大家讲解图论相关的知识,等基础算法篇更新完之后,博主会每周更新一些自己做过的比较好的算法题和他们的技巧。

搜索

搜索也叫做暴搜,在未优化前就是通过穷举所有情况来找到最优解

搜索一般分为深度优先搜索和宽度优先搜索

一般用到的优化方法是:回溯和剪枝

回溯:在搜索过程中,遇到走不通或者走到底的情况时,就回头

剪枝:在搜索过程中,剪掉重复出现或者不是最优解的分支

用的数不重复的用排列组合思想去分析题(像eg:高中的C和A类型的题)

深度优先搜索-DFS(递归型枚举)

实现方法:(用全局变量标记每一步干了啥)+回溯来实现dfs

辅助理解:画决策树

递归型枚举这类题的数据范围都很小–可以当做一个题眼

洛谷 B3622 枚举⼦集(递归实现指数型枚举)
洛⾕ P1706 全排列问题
洛谷 B3623 枚举排列(递归实现排列型枚举)

1.例题: 洛谷  B3622 枚举⼦集(递归实现指数型枚举)
#include <bits/stdc++.h>
using namespace std;
int n;
string path;//全局变量,记录递归过程中,每一步的决策
void dfs(int pos)
{
 if(pos>n)
 {
 cout<<path<<endl;
 return;
  }

//不选
path+='N';
dfs(pos+1);
path.pop_back();//回溯,一般在这个eg:dfs(pos+1)后面会用
//其他类型的数据的话,一般用vector存,好尾删

//选
略
int main()
{
 cin>>n;
dfs(1);
return 0;
 }

2.排列枚举
例题: 洛谷  B3623 枚举排列(递归实现排列型枚举)
     洛⾕   P1706 全排列问题
都需要用到 bool st[N]去标记哪些已经选过了
        一般会在回溯后面加上st[i]=false(表示选过了);//这是剪枝

剪枝与优化(dfs的)

洛谷 P10483 ⼩猫爬⼭
洛谷 P1464 Function

在dfs中,有几种常见的剪枝方法:

1.排除等效冗余:

如果在搜索过程中,通过某⼀个节点往下的若⼲分⽀中,存在最终结果等效的分⽀,那么就只需要搜索其中⼀条分⽀。

(比如:组合中的12和21)

2.可行性剪枝:

如果在搜索过程中,发现有⼀条分⽀是⽆论如何都拿不到最终解,此时就可以放弃这个分⽀,转⽽搜索其它的分⽀。

(比如:组合中的11)

3.最优化剪枝:(在找最优解时会用)

如果在搜索过程中,发现某⼀个分⽀已经超过当前已经搜索过的最优解,那么这个分⽀往后的搜索,必定不会拿到最优解。此时应该停⽌搜索,转⽽搜索其它情况。

4.优化搜索顺序:(在找最优解时会用)

在有些搜索问题中,搜索顺序是不影响最终结果的

此时,应当先选择⼀个搜索分⽀规模较⼩的搜索顺序,快速拿到⼀个最优解之后,⽤最优性剪枝剪掉别的分⽀。

例题: 洛谷 P10483 ⼩猫爬⼭

5.记忆化搜索:(有非常多完全相同的子问题时用此)

(可以通过增加形参来让子问题变得相同)

记录每⼀个状态的搜索结果,当下⼀次搜索到这个状态时,直接找到之前记录过的搜索结果。这也解决了以前遇到大量重复运算不能用递归的场景

例题: 洛谷 P1464 Function

记忆化搜索的注意事项:
1.备忘录中不能一开始就出现递归过程中有可能出现的值
2.递归返回的时候,先把值先存到备忘录里面
3.递归的时候,先往备忘录里面瞅一瞅(不要用成还没初始化的值了)

温馨提示:有些题对剪枝的位置也要要求(可从树状图看出)

例题:洛谷 P1025 [NOIP2001 提⾼组] 数的划分

宽度优先搜索(BFS)

BFS常用来解决边权为1的最短路问题

eg:在二维中至少走多少步才能到…

例题 洛谷 P1443 ⻢的遍历

bfs题在写代码的时候:(dfs才是递归)
常用 queue<pair<int,int>>q来存坐标
表示走了多少步的int  dist[N][N];
在while(q.size())里面去循环

多源BFS

当问题中存在多个起点⽽不是单⼀起点时,这时的最短路问题就是多源最短路问题。

在多源最短路问题中,边权为1的话就可以用多源BFS

牛客网 矩阵距离

把这些源点汇聚在⼀起,当成⼀个"超级源点"就变成了单源BFS
即 1.初始化的时候,把所有的源点都加⼊到队列⾥⾯;
2. 然后正常执⾏ bfs 的逻辑即可。
例题:牛客网  矩阵距离

01 BFS

感觉跟背包那的01问题差距还是大

这个01BFS是"走路"问题

在BFS过程中,把边权为0的扩展出来的点放在队首,把边权为1的扩展出来的点放到队尾(核心思想)

01BFS相较于其他BFS,如果遇到已经遍历过的结点,有可能会找到一条更优的路径

(上面的核心思想体现了这个)

洛谷 P4554 ⼩明的游戏

例题 洛谷  P4554 ⼩明的游戏

Floodfill问题

本质是在寻找具有相同性质的联通块

洛谷 P1596 [USACO10OCT] Lake Counting S

例题: 洛谷  P1596 [USACO10OCT] Lake Counting S
其中的主要代码:
dx[] dy[]是可以走的那几个方向
//给联通的地方打上标记
void dfs(int i, int j)
{
 st[i][j] =true;
 for(int  k = 0;k<0;k++)
 {
 int  x = i + dx[k], y = j + dy[k];
if(x >= 1  && x <= n && y >= 1  && y <= m && a[x][y] == 'W'  && st[x][y] == false)
 dfs(x,y)
  } 
 }
int main()
{
  ...
if(a[i][j]=='W'&&st[i][j] ==false)
 {
ret++;
dfs(i,j);
  }
}

一些比较杂但是做题会用到的知识

洛谷 P1784 数独

有时会用到大坐标的方法:
例题:洛谷  P1784 数独
eg: 本为a[i][j],然后搞一个b[i/3][j/3][num] = true;(举例:i,j为9)
表示在a中的数num在[i/3][j/3]这个3x3方格中

洛谷 P1443 ⻢的遍历

如果一个坐标会一变多一直这样的话
可以用eg:queue<pair<int,int>> q;这些来存坐标
eg: 洛谷  P1443 ⻢的遍历
二维坐标转一维的方法:(二维的下标最好从0开始)
可以将nxm的矩阵坐标(x,y),映射成一个数pos,可以起到空间优化的效果
公式: pos = x*m+y;
      x =pos/m;
      y = pos%m; 

在这里插入图片描述

牛客网 矩阵距离

常用思路:
正难则反
例题: 牛客网 矩阵距离
在此题表现为:
如果针对一个点,直接去找最近的1,那么就需要对所有0来一次bfs,但是时间复杂度太大了
因为去想从1向外扩展,每遍历到0就更新一下最短举例,这样就只用一次bfs

洛谷 P1162 填涂颜⾊

消消乐思想:
如果要标记外围同数据(eg:都为0)但是这些数据又很分散的话,解决这个的方法:
我们可以把整个矩阵的外围包上⼀层0 ,这样只⽤从(0,0)位置开始搜即可
例题:洛谷  P1162 填涂颜⾊
字符映射成连续数字的方法:
小写字母:0-25    ch-'a'
大写字母:26-51   ch-'A'+26
数字: 52-61    ch-'0'+52
应用eg;字典树那的path

洛谷 P5662 [CSP-J2019] 纪念品

股票问题中的重要结论:
任意一笔跨天的交易,都可以转化成连续的"某天买,隔天卖"的形式
例题:洛谷  P5662 [CSP-J2019] 纪念品

洛谷 P5322 [BJOI2019] 排兵布阵

如果需要让列排序的话,必须要先转换成行,然后用sort
例题:洛谷  P5322 [BJOI2019] 排兵布阵
for(int k = i;k<j;k++)这里循环j-i次

牛客网 丢手绢

处理环形问题的常用技巧:
倍增--即复写(这里的倍增不是指前面的倍增算法)
例题: 牛客网  丢手绢
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值