本日主要内容就是搜索(打暴力
搜索可以说是OIer必会的算法,同时也是OI系列赛事常考的算法之一。
有很多的题目都可以通过暴力搜索拿到部分分,而在暴力搜索的基础上再加一些剪枝优化, 就有可能会拿到更多的分数。
有句话说的好嘛,骗分过样例,暴力出奇迹。
真的可以出奇迹的,只要你用得好。
 
1.搜索的概念
在一个给定的空间内,运用一定的查找(遍历)方式,直到找到目标解(状态)的过程,我们称之为搜索。
搜素是尝试性的,搜索是无脑的,搜索是朴素的,搜索在很多时候是显然的,搜索应该总是暴力的。但搜索也是很常用的。
通常我们要把搜索的解空间划分成几个阶段(可能是抽象的),或者说是若干个状态,然后去按层或者按深度进行遍历,并尝试寻找可能的解。
搜索树。何为搜索树?我们想象一个搜索的阶段,首先,我们在初始位置作为起点开始我们的搜索过程,每到达下一个阶段,这棵树便会向下扩展。在同阶段扩展其他节点,这棵树便会拥有分叉,直到我们遍历完整个解空间,我们搜索的整个过程便记录在了搜索树里,而我们要找的解应该就在搜索树的某个叶子节点上,或者宣告无解。
 
2.常见的几类搜索问题
    • 排列问题:枚举1~n的排列
    • 组合问题:少年,你的这个物品,是要选呢?还是不选呢?
    • 路径问题:神奇海螺告诉我,下一步我该怎么走?
 
3.编写搜索算法时我们应该考虑到的问题
    • 我应该用什么搜索算法
    • 我这样写能不能保证一定出解
    • 我这样写找到的解是不是最优
    • 如果不是最优,那要如何才能找到最优解
    • 求解的效率如何(别想太多,搜索的时间复杂度一般都是指数级
 
我重点说一下深度优先搜索(DFS)和广度优先搜索(BFS)。
还有迭代加深算法和A*等略微丧病的搜索,我也会提一下
(今年没讲双向广搜啊。。
(当然也会有爬山法,模拟退火法,遗传算法等玄学搜索算法,仅供装逼
 
4.DFS
不撞南墙不回头,撞了就掉头,一冲到底不服输。
(好了DFS讲完了
我才没有那么不负责任。。
深度优先搜索,我们的思路就是只要能扩展,就一直向深处扩展,直到走不动,这时候要进行一步「回溯」操作。这个操作通常用递归来进行,当然也有非递归形式,不过不常用。
深度优先搜索的优点是代码量通常相对比较少,框架相对固定,而且比较容易理解,占用空间较小。在数据量比较少的时候可以很快出解。
但它的缺点也是明显的,在数据量大的时候遍历整个解答树会非常慢,递归层数过多也有可能引起爆栈。并且,找到的第一个解在某些时候并不一定是最优解,而只是可行解之一。
很多书上喜欢给一个DFS的大体框架,用来告诉初学者这种算法大概长什么样。那我也给出一个大体的DFS框架吧。。
 void dfs(int dep){
if (dep==n+){
//执行输出操作
return;
}
//可以在这里加一些特殊边界什么的,最后直接return就好
for 枚举每个可能的位置或者决策或者方案{
//可以在这里加一些剪枝什么的 if (这个方案合法){
打标记
dfs(dep+);
删除标记//这个时候已经回溯了
} }
}
 
其实不难理解。递归进入下一层,判断是否已经到达边界,如果还不是边界就按照一定的规则,取遍所有可能的方案,作为当前状态。
同时我们可能需要「打标记」操作方便我们「回溯」。打标记,便是记录某个数或者某个点是否走过,当我们回溯的时候应该把这个标记擦掉,避免影响之后的搜索。
多思考思考。
举个例子吧。99%的OIer都会的DFS:生成全排列
我会我会!next_permutation!
。。。把那个用STL的拖出去毙了,这里是搜索专场!
 void dfs(int now) {
if (now == n) {
for (int i = ; i < n; i++)
cout << a[i] << " ";
cout << endl;
return;
}
for (int i = ; i <= n; i++)
if (!check[i]) {
a[now] = i;
check[i] = true;
dfs(now + );
check[i] = false;
}
}
 
当然,生成全排列也可以用栈,非递归地完成。不过这不常用。
 
思考:如果我要生成1~n的组合呢?
 
 
再来一个99%的OIer都见过的例题,八皇后。
下过国际象棋吧?没下过也没问题,这题不需要你了解多少国际象棋规则。
一个8*8的棋盘,摆放八个皇后,要求这八个皇后中的任意两个不能位于同行同列同对角线,求方案数。
答案是92.
如果你打算实现一下这个搜索解的过程,并且不加任何的优化,也就是说,把八个皇后在所有可能的位置都枚举一遍,并判断是否可行。我可以说的是,上述方法完全正确,但是。。这程序可能要跑个一两年吧。。。
考虑一下优化。既然要求任意两个皇后不能位于同行同列同对角线,那么我们在放第n+1个皇后时,肯定不能在第n个皇后的同一行和同一列,在搜索即将要扩展至此的时候肯定不可行,我们就应该「回溯」。这样只需要判断任意两个皇后是否处在同一对角线就好了。
给一个伪代码:
 void dfs(int dep){
if (dep==n+){
//输出方案
return;
}
for 枚举第dep行的皇后的可能位置{ if (这个位置合法){
打标记
dfs(dep+);
删除标记
} }
}
 
5.Floodfill算法
又叫灌水法,填充法。
来一道例题:有一个n * m的点阵,有一些点是陆地,其他点是海洋,一共有多少块陆地?
这个算法其实类似于DFS,若要找到某个点所在的起点,就以此点开始DFS,遍历与它联通的所有点,如果枚举到的点没有被访问过并且是陆地,就可以打标记扩展。
 
6.BFS
按层搜索,广度优先,队列储存,首解最优。
其实可以这样想一下遍历解答树的过程:从根节点出发,找到所有与之相连的第一层节点,依次遍历,遍历的同时找到扩展到的下一层节点,储存起来方便下一步搜索,这样就是一个正常向的BFS过程。
一层一层地慢慢展开,每个节点到根节点的距离是慢慢增加的,而不是像DFS那样一直走到最深处再考虑往回走,所以BFS找到的第一个解一般就是最优解。
缺点的话,占用空间是比DFS大一些的,而且框架比DFS要长一些。。
之前说到的拓扑排序,便是一个BFS的过程。
   大致的框架:
  

 void bfs(int s){
queue<int> q;
q.push(s);
vis[s] = true;
while (!q.empty()){
int u = q.front();
q.pop();
for (遍历所有与u相邻的节点){
if (当前找到的扩展节点为解){
执行输出操作
} if (!vis[u]){
q.push(u);
vis[u] = true;
}
}
}
return ;
}
  
然后就是有一些对DFS和BFS进行优化的算法,不过实际应用当中用的比较少,我稍微写一下思想。
 
7.迭代加深搜索(就是那个所谓的A*
其实可以把它理解为集DFS占空间小和BFS首解最优两种特性于一体的一种搜索算法。先给DFS一个比较小的深度限制,然后逐渐增加深度限制,直到找到解或找遍所有分支为止。
 
8.启发式搜素
有效摒弃了DFS与BFS的无脑搜索缺点。利用一个“预判”引导搜索方向,就好像人走迷宫预先判断哪里很显然是死路那样,减小搜索范围。
启发式搜索的强度取决于我们“预判”的程度。
如果预判程度太高,虽然能大大减小工作量,但会有可能把本来应该是能找到最优解的道路剪掉,导致我们找不到最优解。
如果预判程度太低,会导致事倍功半,性能上与BFS差不了多少但是写起来可比BFS麻烦多了。所以我们要尽量合理地使用启发方式。
其实这里有一个评价函数f,用来评估我们的行走决策。这里只给出思想,如果有想深入了解此算法的同学可以自行查阅相关资料。
 
9.(据说可以拿来装逼的算法
爬山法,模拟退火,遗传算法
我当时是懵逼的。这我真不会。
 
概念什么的写的很少就是了。。搜索这东西得拿题来多写才行。。
也有可能是我太弱了吧。。
 
 

夏令营讲课内容整理 Day 4.的更多相关文章

  1. 夏令营讲课内容整理 Day 7.

    Day7是夏令营的最后一天,这一天主要讲了骗分技巧和往年经典的一些NOIP试题以及比赛策略. 这天有个小插曲,上午的day7T3是一道和树有关的题,我是想破脑袋也想不出来,正解写不出来就写暴力吧,暴力 ...

  2. 夏令营讲课内容整理 Day 3.

    本日主要内容是树与图.   1.树 树的性质 树的遍历 树的LCA 树上前缀和   树的基本性质: 对于一棵有n个节点的树,必定有n-1条边.任意两个点之间的路径是唯一确定的.   回到题目上,如果题 ...

  3. 夏令营讲课内容整理Day 0.

    今年没有发纸质讲义是最气的.还好我留了点课件. 第一次用这个估计也不怎么会用,但尝试一下新事物总是好的. 前四天gty哥哥讲的内容和去年差不多,后三天zhn大佬讲的内容有点难,努力去理解吧. 毕竟知识 ...

  4. 夏令营讲课内容整理 Day 6 Part 3.

    第三部分主要讲的是倍增思想及其应用. 在Day3的整理中,我简要提到了倍增思想,我们来回顾一下. 倍增是根据已经得到的信息,将考虑的范围扩大一倍,从而加速操作的一种思想,它在变化规则相同的情况下,加速 ...

  5. 夏令营讲课内容整理 Day 6 Part 2.

    Day 6的第二部分,数论 数论是纯粹数学的分支之一,主要研究整数的性质   1.一些符号: a mod b 代表a除以b得到的余数 a|b a是b的约数 floor(x) 代表x的下取整,即小于等于 ...

  6. 夏令营讲课内容整理 Day 6 Part 1.

    Day6讲了三个大部分的内容. 1.STL 2.初等数论 3.倍增   Part1主要与STL有关. 1.概述 STL的英文全名叫Standard Template Library,翻译成中文就叫标准 ...

  7. 夏令营讲课内容整理 Day 5.

    DP专场.. 动态规划是运筹学的一个分支, 求解决策过程最优化的数学方法. 我们一般把动态规划简称为DP(Dynamic Programming)   1.动态规划的背包问题 有一个容量为m的背包,有 ...

  8. 夏令营讲课内容整理 Day 2.

    本日主要内容是并查集和堆. 并查集 并查集是一种树型的数据结构,通常用来处理不同集合间的元素之间的合并与查找问题.一个并查集支持三个基本功能:合并.查找和判断.举一个通俗的例子,我和lhz认识,lhz ...

  9. 夏令营讲课内容整理Day 1.

    主要内容是栈和队列. 1.  栈 运算受到限制的线性表.只允许从一端进行插入和删除等操作.这一端便是栈顶,另一端便是栈底. 其实可以把栈想象层任何有底无盖的柱状的容器...毕竟栈满足后进先出的特性.计 ...

随机推荐

  1. Big Event in HDU(多重背包套用模板)

    http://acm.hdu.edu.cn/showproblem.php?pid=1171 Big Event in HDU Time Limit: 10000/5000 MS (Java/Othe ...

  2. CSS3技巧巧妙使用:not(:last-of-type)简化你的css代码

    终于找到了一个好方法,使用:not(:last-of-type)简单方便,再也不要麻烦的单独使用:last-of-type了,不错! 应用场景:平时我们的列表一般都会有分割线,但是最后一个列表没有分割 ...

  3. ItemCF_基于物品的协同过滤_MapReduceJava代码实现思路

    ItemCF_基于物品的协同过滤 1.    概念 2.    原理 如何给用户推荐? 给用户推荐他没有买过的物品--103 3.    java代码实现思路 数据集: 第一步:构建物品的同现矩阵 第 ...

  4. 学习总结:libevent--简单入门

    libevent--简单入门 一.简介 libevent是一个c语言写的事件驱动库,轻量级,专注于网络,跨平台特性好,支持多种 I/O 多路复用.支持I/O,定时器和信号等事件,允许设置注册事件优先级 ...

  5. P1144 最短路计数

    P1144 最短路计数 题目描述 给出一个N个顶点M条边的无向无权图,顶点编号为1-N.问从顶点1开始,到其他每个点的最短路有几条. 输入输出格式 输入格式: 输入第一行包含2个正整数N,M,为图的顶 ...

  6. shopnc前台登陆不进去解决方法

    安装好shopnc后,注册新用户成功,且登陆后提示登陆成功,但是一两秒后自动跳转回登陆页面,需要重新登陆问题 PHP写session不是自动起的,需要修改后才行,所以 找到PHP的php.ini配置文 ...

  7. pythhon_如何读写json数据

    案例: 在web应用中常常用到json数据进行传输数据,本质上是字典类型数据转换成字符串,通过字符串进行网页传输,然后把接收到的字符串转换成字典类似数据 需求:实现字典转换成字符串,字符串转换成字典数 ...

  8. linux ssh登录的小知识

    查看服务器的各个端口: # netstat -tulnp 或者 #netstat -tnip 筛选在后面添加 |grep *** 准许root登录 #vi /etc/ssh/sshd_config 找 ...

  9. docker搭建私服

    拉registry镜像 假设在192.168.100.17服务器上搭建私服 docker pull registry docker run -d -v /data/docker/registry:/v ...

  10. 04_Javascript初步第二天(下)

    错误对象 try{ aa();//这是一个未被定义的方法 }catch(e){ alert(e.name+":"+e.message);//输出:ReferenceError:aa ...