搜索
d f s \rm dfs dfs
常用 v o i d \rm void void 返回值类型的函数,通常格式如下:
void dfs(一些参数) {
if (找到答案) {
判断最优解/记录答案;
return ;
}
for (枚举这个所有选项) {
if (这个选项是合法的) {
记录下修改的值(保存现场);
dfs(修改后的参数);
恢复原来的值(恢复现场);
}
}
return ;
}
d f s \rm dfs dfs 适用于找到一种解的问题。
b f s \rm bfs bfs
既可以直接在主函数内实现,也可以自定义一个函数,通常格式如下:
int vis[长度]; // 标记数组,也用于存放答案
数据类型 bfs() {
queue<数据类型> q;
q.push(初始状态);
while (!q.empty()) {
数据类型 cur = q.front();
q.pop();
if (cur是答案) return cur;
for (枚举所有扩展) {
if (这个选项是合法的) {
vis[这个选项] = vis[cur] + 1;
q.push(这个选项);
}
}
}
return 数据类型中表示无解的值;
}
b f s \rm bfs bfs 一般用于求在地图中的最快到达问题,但是需要消耗更多的空间。
搜索优化技巧
优先队列存储结点
在有可以通过但是需要耗费更多时间的结点时,如果我们像普通的
b
f
s
\rm bfs
bfs 一样进行扩展,那么我们所找到的第一个结点就不一定是耗时最少的结点,从而要全部扩展结束才能知道最优解。这样的方法太慢了,所以我们可以用优先队列来存储结点,那么我们每次选出的结点一定是耗时最短的结点,所以我们第一次找到的答案就一定是最优解。
警示后人:重载运算符需要使用友元函数定义在结构体里。
奇偶性剪枝
对于棋盘(地图)上正好走
n
n
n 步到达终点的问题,可以对棋盘进行01染色,如下为
3
×
3
3\times3
3×3棋盘的染色:
0
1
0
1
0
1
0
1
0
1
0
1
\begin{array}{|c|c|c|} \hline 0 & 1 & 0\\\hline 1 & 0 & 1\\\hline 0 & 1 & 0\\\hline 1 & 0 & 1\\\hline \end{array}
010110100101
如果起点与终点染上的数字相同,则两点之间需要用偶数步才能抵达;反之,则需要用奇数步。
例题,在此之中需要刚好花费 t t t 步走到门,这样我们在每个节点进行判断奇偶性即可。
双向广度优先搜索
一般地,我们使用的是单向广度优先搜索,就是仅从起点开始向后去扩展结点。
但是这样的话,我们的结点数量将会极为巨大,时间复杂度也是
O
(
n
2
)
O(n^2)
O(n2) 或是
O
(
n
!
)
O(n!)
O(n!) 的,效率极低且有可能会
M
L
E
\rm MLE
MLE,这样就十年
O
I
\rm OI
OI 一场空了!!!
所以,我们智慧的前辈改进了单向广度优先搜索,发明出了双向广度优先搜索。
双向广度优先搜索好闪,拜谢双向广度优先搜索!
双向广度优先搜索适用于起点与终点皆已知的问题中,从起点和终点同时向中间枚举,当中间出现相遇的情况时我们就可以认定我们已经找到了最短路。
以下这张图可以更加直观地看出两种搜索的差距:
使用方法:
- 定义一个二维标记数组 v i s [ 2 ] [ n ] vis[2][n] vis[2][n],第一维存起点出发扩展出来的结点标记,第二维则存从终点出发扩展出来的结点标记。
- 定义两个队列 q 1 q1 q1 和 q 2 q2 q2, q 1 q1 q1 存起点所扩展的结点, q 2 q2 q2 存终点所扩展的结点。
- 当两个队列都非空时(
q1.size() && q2.size()
),那么选择元素较少的一个队列进行扩展。 - 当 v i s [ 0 ] [ k ] = v i s [ 1 ] [ k ] vis[0][k]=vis[1][k] vis[0][k]=vis[1][k] 时,那么从起点出发的路径与从终点出发的路径交接了,那么我们就找到了一条从起点前往终点的通路。
A ∗ A* A∗ 算法
A
∗
A*
A∗ 算法中确定了一个估价函数(也称启发式函数),用以引导搜索。因为不同状态的估价不同,所以
A
∗
A*
A∗ 算法中使用 priority_queue
这一数据结构来存储每个状态。那么估价高的状态就会最先被访问到,如果估价函数设计合理,那么第一次找到的答案就是最优解。
曼哈顿距离
曼哈顿距离是一种用于地图搜索中的常见的距离表示函数,它是这样计算距离的:
S
=
∣
x
1
−
x
2
∣
+
∣
y
1
−
y
2
∣
S=|x_1 - x_2| + |y_1-y_2|
S=∣x1−x2∣+∣y1−y2∣
与欧几里得距离不同
练习
动态规划
- 解题套路
- 确定状态
一个函数符号 f ( x ) f(x) f(x),外加这个函数符号的含义描述。
一般函数所对应的值,就是要求解的值。 - 确定状态转移方程
确定 f ( x ) f(x) f(x) 究竟依赖于那些 f ( y ) f(y) f(y) 的值,之间如何转移。 - 分析边界条件
- 程序实现
- 确定状态