开拓计划2 - 搜索

开拓计划2 - 搜索

搜索概述

搜索的原理

  • Q:搜索的原理是什么?
  • A:搜索(search),是指充分利用计算机的高性能特点来进行对答案的不断尝试的过程。

搜索在OI中的作用

  • Q:搜索有哪些作用?
  • A:搜索在题目时间比较宽松的时候适合使用,主要能解决枚举类问题。

搜索的本质

  • Q:搜索的本质是什么?
  • A:搜索的本质就是分类讨论,遇到一个分叉就无论如何都要走一次,直到讨论出合理的结果。

DFS(深度优先搜索)

原理

  • Q: DFS的原理是什么?
  • A: DFS的原理是:对于每种情况都一直走到底,直到遇到障碍,就回到上一个分叉点,讨论下一种情况。
  • Q: DFS和栈有什么关系?
  • A: DFS实际上是一个栈,每一个等待搜索的元素都进去然后就拿出来处理、放回栈中,循环往复,直到这个元素处理完。
  • Q: DFS和递归有什么关系?
  • A:系统本身在递归时也调用了栈,与DFS异曲同工,所以DFS一半以递归形式呈现。
  • Q: DFS和树有什么关系?
  • A: DFS和树本身没什么关系,树只是和递归在一起时出现的衍生品,但是如果题目需要遍历一棵树或图,就会用到DFS,在每一个节点都可以向它的几个叶子节点扩展。在进行DFS的时候,也可以把每种情况都化成树,来辅助理解。

比如:

这时如果要枚举排列组合在 \(1\)\(2\) 号节点处都有两种选择,需要分别讨论。

枚举顺序:124(2)56(5)(2)(1)37(3)(1)。

代码

void dfs(int x){
    if(x达到边界) 更新,return;
    for(枚举所有的情况) if(满足条件) 打标记,dfs(x+1),手动回溯
}

注意事项

  1. 注意手动回溯,到底该不该回溯,如果不用(只需要可行性,不用具体方案和方案数),就不用对标记手动回溯。
  2. 注意在向下递归的时候要全面讨论每种情况。

NKOJ 2165【搜索】四色问题

思路:搜索每一个国家能用的颜色。

实现方法:

  • 当前枚举到第 \(x\) 个国家,如果它和与它邻接的国家用了同一种颜色,那这种颜色肯定不行。否则,就可以继续递归。
  • 由于是顺序枚举,第一次枚举到的一定是字典序最小的。

注意事项:

  1. 在标记每个国家用了什么颜色时,一定要注意清空标记(回溯)。

NKOJ 2166 【搜索】工作安排

思路:搜索每个人干的工作。

实现方法:

  • 当前枚举到第 \(x\) 个人,如果这份工作被别人给干了,那他就换一份。
  • 统计最大的答案。

注意事项:

  1. 枚举从第 \(0\) 个人开始,因为第一个人也要讨论,但要注意到第 \(n\) 个人就结束,如果是手动枚举第 \(1\) 个人,就要枚举 \(n+1\) 次。

剪枝

剪枝的概念及作用

  • Q:什么是剪枝?
  • A:剪枝,是指通过减少不必要的搜索(修建递归树的枝)来达到节省时间的目的。

剪枝的方法

  1. 可行性剪枝:如果到目前为止的数据都可以确定数据不可行,就不用递归下去。
  2. 最优性剪枝:如果当前枚举到的答案已经大于目前的最优解,直接舍去。
  3. 优化搜索顺序:有时递归树可能很长,通过优化搜索顺序,从本质上改变搜索树的形态。
  4. 排除等效冗余:在合适的地方替换为贪心等方法,能够更快地找到答案,而不是继续递归下去。

NKOJ 3189 逃离迷宫3

思路:DFS+剪枝

实现方法:

  • 先写出普通的DFS代码。
  • 由于数据被加强了,必须剪枝。
    • 采用最优性剪枝
    • 如果走到 \((x,y)\) 的最小步数大于已有的步数,继续搜索毫无意义,直接跳过。

注意事项:

  1. 一定注意打标记和回溯对称。

BFS(广度优先搜索)

原理

  • Q: BFS的原理是什么?
  • A: BFS的原理是:对于每一种可能的情况每一次枚举都向下深入一层,如果遇到障碍就直接删除这种可能性。
  • Q: BFS和队列有什么关系?
  • A: BFS的处理顺序就是一个队列结构,每遇到一个节点就将它加入队列,等待下一次处理,处理完了又放回去,如果不可能就不放回队列。
  • Q: BFS和树有什么关系?
  • A: BFS相当于在遍历一棵树时采用逐层遍历,从一个结点开始发散到当前节点的子节点的遍历方式。

上图的遍历顺序:1234576。

  • Q: BFS和DFS有哪些差别?
  • A: BFS能快速算出到终点的方法数、以及最短路径但遍历顺序的字典序不一定最优,由于不用像DFS那样回溯,时间效率优于DFS,但是空间较大。DFS能找到最小的遍历字典序但路径不一定最短,还需要反复回溯才能找到方案数,时间复杂度堪忧,但是空间消耗较少,一般在遍历二叉树时用。

代码

void bfs(int st){
    que.push(st);
    while(!que.empty()){
	        int tp=que.front();
	        if(tp到达边界) break;
	        for(枚举所有的情况) if(满足条件) 打标记,que.push(tp+1);
	}
}

注意事项

  1. BFS在使用时务必关注空间,如果不行就用DFS+剪枝。
  2. 当前步数可以存在一个数组,也可以和数字一起放入队列,根据空间来。
  3. BFS也常常用来做洪水填充算法,从一个固定的点开始,将某种标记标到力所能及的最远处。
    注:洪水填充也可以用不带回溯的 DFS 来实现。

NKOJ 1086 细胞分裂

思路:BFS洪水填充功能的典型应用。

实现方法:

  1. 遍历输入数组,如果当前位置没到过并且是一个细胞,就开始用BFS检查其势力范围,并给答案 \(+1\)
posted @ 2024-12-14 09:43  hsr_ray  阅读(27)  评论(0)    收藏  举报