C++算法学习笔记(更新中)

搜索

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 一场空了!!!
所以,我们智慧的前辈改进了单向广度优先搜索,发明出了双向广度优先搜索。
双向广度优先搜索好闪,拜谢双向广度优先搜索!
双向广度优先搜索适用于起点与终点皆已知的问题中,从起点和终点同时向中间枚举,当中间出现相遇的情况时我们就可以认定我们已经找到了最短路。

以下这张图可以更加直观地看出两种搜索的差距:
单向与双向  的显著差异

使用方法:

  1. 定义一个二维标记数组 v i s [ 2 ] [ n ] vis[2][n] vis[2][n],第一维存起点出发扩展出来的结点标记,第二维则存从终点出发扩展出来的结点标记。
  2. 定义两个队列 q 1 q1 q1 q 2 q2 q2 q 1 q1 q1 存起点所扩展的结点, q 2 q2 q2 存终点所扩展的结点。
  3. 当两个队列都非空时(q1.size() && q2.size()),那么选择元素较少的一个队列进行扩展。
  4. 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=x1x2+y1y2
与欧几里得距离不同

练习

说了这么多,就来试试你的实力吧!
题单1
题单2
题单3

动态规划

  • 解题套路
    1. 确定状态
      一个函数符号 f ( x ) f(x) f(x),外加这个函数符号的含义描述。
      一般函数所对应的值,就是要求解的值。
    2. 确定状态转移方程
      确定 f ( x ) f(x) f(x) 究竟依赖于那些 f ( y ) f(y) f(y) 的值,之间如何转移。
    3. 分析边界条件
    4. 程序实现
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

三日连珠

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值