注:此博文是在老师上课之后总结的,属于课堂笔记,大部分摘自老师课件。
引言
根据“信息学初学者之家”网站的统计,Ural(俄罗斯的Ural州立大学的简称 ,有名的Ural Online Problem Set 就是该校的系统)的题目类型大概呈如下的分布:
搜索 动态规划 贪心 构造 图论
约10% 约15% 约5% 约5% 约10%
计算几何 纯数学题 数据结构 其它
约5% 约20% 约5% 约25%
搜索分类
盲目搜索:按预定的控制策略进行搜索,在搜索过程中获得的中间信息不用来改进控制策略。
1. 广度优先搜索(Breadth First Search)
2. 深度优先搜索(Depth First Search)
3. 纯随机搜索、重复式搜索、迭代加深搜索、
迭代加宽搜索、柱型搜索
启发式搜索:在搜索中加入了与问题有关的启发性信息,用以指导搜索朝着最有希望的方向发展,加速问题的求解过程并找到最优解。
1. A*算法
2. IDA*算法
深搜DFS与回溯法与广搜辨析
概念
深搜即深度优先搜索,总结一句话是不撞南山不回头。就是搜索按一定限制条件,往深处搜索,直到不能此路不能搜索再换路,直到所有部分全搜索完。
回溯法又叫试探法,我觉得他和深搜类似,不过,回溯一般是用栈的形式,记录每次每一步,不满足前进的步就退回(即出栈),这种走不通就退回再走的技术为回溯法。
广搜即广度优先搜索,就是按层次搜索,一层层来,但是顺序是有要求的,就是上一层按什么顺序搜索,下一层也要按这个顺序层次搜索。
话不多说,看下图搜索的箭头方向,方便记忆。
深搜与广搜区别
- 相同点:都是对于树状的搜索树进行,都可判定解的存在性和总数。属于粗放型的搜索,接近于暴力解决。
- 不同点:
- 深度优先搜索:利用栈这种数据结构来实现(利用递归便于实现,但是效率较低),找到的第一个解不一定是最优解,只是先序遍历最早的可行解。
- 广度优先搜索:利用队列这种数据结构来实现,找到的第一个解经常都是最优解(如迷宫的最短路径),但是不常用来求所有解(重复的次优解常常都被剪枝剪掉了)。
DFS代码基本框架
递归
void DFS(int k) //处理第k步
{
if(k==n) //已经处理到第n步,到达目的状态
输出结果
else{//处理第k步
for (int i=1; i<=m; i++) //第k步中有m种可能
{
处理第k步
DFS(k+1);//进入第k+1步
}
}
}
非递归
//定义一个栈;
//起始点加入栈;
while(栈不空)
{
//取出栈顶结点;
//若它是所求的目标状态,跳出循环;
//否则,从它扩展出子结点,全都添到栈顶;
}
//若循环中找到目标,输出结果;
//否则输出无解;
BFS代码基本框架
- BFS关键问题:判重。
- BFS主要试用于搜索最短距离,即逐层平行搜索。
void BFS()
{
//初始化队列
while(队列不为空且未找到目标节点)
{
//取队首节点扩展,并将扩展出的节点放入队尾;
//必要时要记住每个节点的父节点;
}
}
拓展之(DBFS)双向广度优先搜索
- DBFS算法是对BFS算法的一种扩展。
- 案例见:Knight Moves
- DBFS算法从两个方向以广度优先的顺序同时扩展,一个是从起始节点开始扩展,另一个是从目的节点扩展,直到一个扩展队列中出现另外一个队列中已经扩展的节点,也就相当于两个扩展方向出现了交点,那么可以认为我们找到了一条路径。即是起点和终点同时出发,逐层推进,相遇时,将两个方向走的步伐相加。
- 注意要交替逐层搜索,要先检查该方向该层所有结点再交替。
//双向广搜代码框架
struct State {}; //状态
queue<State>que[2];
bool flag;
void bfs(int d) {
//取队首节点扩展
State p=que[d].front();que[d].pop();
if(p==goal) flag=true;
else{//将扩展出的节点放入队尾;
//必要时要记住每个节点的父节点;
State q;//新结点
//...新结点的拓展处理
que[d].push(q);//新状态入队
}
}
int dbfs() {
//初始化
int cnt=0;
while(flag) {
cnt=que[0].size();
while(cnt-- &&flag)
bfs(0);
cnt=que[1].size();
while(cnt-- &&flag)
bfs(1);
}
return cnt;
}
最终笔记于2017-6-4;
二次笔记于2017-07-17;
三次笔记于2017-07-27。