常用算法代码模板与题单 | B - 搜索

这篇文档其实是我自己学习路上的一份“复习笔记”。之前看了不少教程,零零散散记了一堆代码片段和小技巧,时间久了就容易忘,翻起来也麻烦。所以干脆花时间整理了一下,把常用的模板、容易踩的坑、还有一些个人觉得挺实用的小技巧都归到了一起。

如果你也在学类似的东西,或许能帮你省下一些翻找的时间;如果有哪里写得不清楚或不对,也欢迎随时指正交流。

希望这份整理也能对你有一点点帮助~ ✨


【递归】

算法模板

void fun(x)
{
	if (..)
	{
		return;  // 递归退出条件
	}
	fun(y);  // 数据拆分
}

算法应用

题目来源标题难度星级考察算法一句话思路总结
AcWing[[96 奇怪的汉诺塔]]
AcWing[[98 分形之城]]
AcWing[[118 分形]]
AcWing[[1152 格雷码]]
AcWing1256 扩展二叉树
AcWing1258 小球
AcWing1259 二叉树遍历
AcWing1260 二叉树输出
AcWing1261 查找二叉树
AcWing3429 全排列
AcWing3483 2的幂次方
AcWing5930 分解因数
AcWing5931 菲波那契数列
AcWing5932 扩号匹配问题
AcWing5933 爬楼梯
AcWing5934 汉诺塔问题
AcWing5937 分数求和
AcWing5939 判断元素是否存在
AcWing5957 黑白棋子的移动
AcWing6049 求后序遍历
AcWing6050 对称二叉树
洛谷B2142 求 1+2+3+…+N 的值入门
洛谷B2144 阿克曼(Ackermann)函数入门
洛谷B2145 digit 函数入门
洛谷B2146 Hermite 多项式入门
洛谷B2147 求 f(x,n)入门
洛谷B2148 再求 f(x,n)入门
洛谷[[P2907 Roads Around The Farm]]入门
洛谷P5739 计算阶乘入门
洛谷P1010 幂次方普及-
洛谷P5657 格雷码普及-
洛谷P5461 赦免战俘普及

【DFS - 一维】

算法模板

void dfs(int u)
{
    if (u==n)
	{  // 到达n层后结束搜索
        for (int i=0; i<n; i++)
		{
			// 按照题目要求执行,或者打印、计算等
        }
        return ;  // 一定要加这句返回
    }
	// 注意:DFS没有通用模板,每道题都有自己的顺序,这里需要根据每道题进行修改
    for (int i=1; i<=n; i++)
	{  // 每个位置可以遍历的内容,如这里是可以填入1~n的数字
        if (!st[i])
		{  // 判断状态,确定是否可以填入
            path[u] = i;
            st[i] = true;
            dfs(u+1);  // DFS搜索
            // path[u] = 0; // 可以不要这句,因为每次都被赋值
            st[i] = false;
        }
    }
}
// 从第0层开始遍历
dfs(0);
// 剪枝方法1
// 增加sum参数,直接传递当前已选数的总和,避免重复计算,从而提高效率
void dfs(int step, int sum)

// 剪枝方法2
// 只要满足一种方案就退出
void dfs(int step)
{
    if (flag) return;
}

算法应用

题目来源标题难度星级考察算法一句话思路总结
AcWing92 递归实现指数型枚举
AcWing93 递归实现组合型枚举
AcWing94 递归实现排列型枚举
AcWing165 小猫爬山
AcWing167 木棒
AcWing168 生日蛋糕
AcWing171 送礼物
AcWing460 子矩阵
AcWing842 排列数字
AcWing1111 字母
AcWing1115 取石子游戏
AcWing1117 单词接龙
AcWing1118 分成互质组
AcWing3428 放苹果
AcWing5940 素数环
AcWing5941 自然数的拆分
AcWing6099 座位
洛谷AT_abc425_b Find Permutation 2入门
洛谷AT_abc356_c Keys普及-
洛谷AT_abc357_c Sierpinski carpet普及-
洛谷AT_abc358_c Popcorn普及-
洛谷AT_abc363_c Avoid K Palindrome 2普及-
洛谷AT_abc367_c Enumerate Sequence普及-
洛谷AT_abc371_c Make Isomophic普及-
洛谷AT_abc374_c Separated Lunch普及-
洛谷AT_abc404_d Goin’ to the Zoo普及-
洛谷AT_abc416_c Concat (X-th)普及-
洛谷AT_abc427_c Bipartize普及-
洛谷B3621 枚举元祖普及-
洛谷B3623 枚举排列(递归实现排列型枚举)普及-
洛谷[[B3850 幸运数]]普及-
洛谷P1036 选数普及-
洛谷P1087 FBI树普及-
洛谷P1157 组合的输出普及-
洛谷[[P1218 特殊的质数肋骨]]普及-
洛谷[[P1460 健康的荷斯坦奶牛]]普及-
洛谷P1706 全排列问题普及-
洛谷P1827 美国血统普及-
洛谷P2036 PERKET普及-
洛谷P2392 kkksc03考前临时抱佛脚普及-
洛谷P2404 自然数的拆分问题普及-
洛谷P2677 Bookshelf 2普及-
洛谷[[P6183 The Rock Game]]普及-
洛谷[[P8185 Blocks]]普及-
洛谷[[P9011 Air Cownditioning II]]普及-
洛谷[[P10377 好斗的牛]]普及-
洛谷P10448 组合型枚举普及-
洛谷AT_abc382_d Keep Distance普及
洛谷B3622 枚举子集(递归实现指数型枚举)普及
洛谷P1019 单词接龙普及
洛谷[[P1118 Backward Digit Sums]]普及
洛谷[[P1215 母亲的牛奶]]普及
洛谷P1228 地毯填补问题普及
洛谷P1259 黑白棋子的移动普及
洛谷P1498 南蛮图腾普及
洛谷P1928 外星密码普及
洛谷[[P5194 Scales]]普及
洛谷AT_abc390_d Stone XOR普及+
洛谷P1034 矩形覆盖普及+
洛谷P1043 数字游戏普及+
洛谷P1310 表达式的值普及+
洛谷P1433 吃奶酪普及+
洛谷P1549 棋盘问题普及+
洛谷P1731 生日蛋糕普及+
洛谷P9126 Moo Route II普及+
洛谷[[P10483 小猫爬山]]普及+
洛谷P1092 虫食算提高+
洛谷P1120 小木棍提高+
洛谷P1312 Mayan游戏提高+
洛谷[[U207723 分成互质组(Coplasm group)]]
AtCoderAT_awc0003_e Cargo Delivery Truck
AtCoderAT_awc0010_e Exhibition Booth Arrangement
学而思编程[[安全密码]]普及奠基⭐⭐DFS 枚举 + 剪枝字典序 3 → 6 → 9 3 \to 6 \to 9 369)逐位 DFS 枚举 n n n 位密码,用全局计数器 cnt 记录已生成的合法密码数;填第 step 位时检查是否与前一位构成禁止组合( 36 36 36 69 69 69 93 93 93),若构成则 continue 剪枝跳过;当 step > ncnt++,若 cnt == k 则输出当前密码并结束;递归深度为 n n n,最坏时间复杂度 O ( 3 n ) O(3^n) O(3n),空间复杂度 O ( n ) O(n) O(n)

【DFS - 二维】

算法模板

算法应用

题目来源标题难度星级考察算法一句话思路总结
AcWing166 数独
AcWing843 n-皇后问题
AcWing1112 迷宫
AcWing1113 红与黑
AcWing1114 棋盘问题
AcWing1116 马走日
AcWing2770 方格取数
AcWing3472 八皇后
AcWing5976 细胞
AcWing6037 连通块
AcWing6046 围成面积
洛谷AT_abc378_d Count Simple Paths普及-
洛谷P1605 迷宫普及-
洛谷[[P1644 跳马问题]]普及-
洛谷AT_abc374_d Laser Marking普及
洛谷AT_abc424_d 2x2 Erasing 2普及
洛谷P1219 八皇后普及
洛谷[[P1671 Rigging the Bovine Election]]普及
洛谷[[P10379 俄罗斯方块]]普及
洛谷P3956 棋盘普及+
洛谷P1074 靶形数独提高+
洛谷U543688 传教士

【BFS - 一维】

算法模板

// 该模板存在问题是队列长度只增不减,可使用vector方式保持队列长度始终在范围内
typedef pair<int, int> PII;  // 三个及以上的参数适合用结构体
PII q[N*N];
int d[N][N];  // 记录每个点到起点的距离
int dx[4]={-1,0,1,0};
int dy[4]={0,1,0,-1};
int bfs()
{
	int hh=0, tt=0;
	q[0] = {0, 0};
	memset(d, -1, sizeof(d));  // 初始化每个点到起点的距离
	d[0][0] = 0;  // 起点的距离为0
	while (hh<=tt)
	{
		auto t = q[hh++];
		for (int i=0; i<4; i++)
		{
			int x = t.first+dx[i], y = t.second+dy[i];
			if (x>=0 && x<n && y>=0 && y<m && g[x][y]==0 && d[x][y]==-1)
			{
				d[x][y] = d[t.first][t.second]+1;
				q[++tt] = {x,y};
			}
		}
	}
	return d[n-1][m-1];
}
bfs()  // 调用bfs(),返回最短路径

算法应用

题目来源标题难度星级考察算法一句话思路总结
AcWing190 字串变换
AcWing1100 抓住那头牛
AcWing6047 奇怪的电梯
洛谷AT_abc424_c New Skill Acquired普及-
洛谷[[P1588 Catch That Cow]]普及-
洛谷[[P2959 The Leisurely Stroll]]普及-
洛谷AT_abc361_d Go Stone Puzzle普及
洛谷AT_abc451_d Concat Power of 2普及
洛谷P1135 奇怪的电梯普及
洛谷P3958 奶酪普及
洛谷P4017 最大食物链计数普及
洛谷P1032 字串变换普及+
洛谷P2730 魔板 Magic Squares普及+
AtCoderAT_awc0031_c Island Hopping Adventure
AtCoderAT_awc0033_c Spread of Rumors
AtCoderAT_awc0035_c Chain Blackout

【BFS - 二维】

算法模板

单源BFS

// 用vector改写
typedef pair<int, int> PII;  // 三个及以上的参数适合用结构体
queue<PII> q;
int d[N][N];  // 记录每个点到起点的距离,这里无需vis数组(dist[][]==-1表示没走过)
int dx[4]={-1, 1, 0, 0};
int dy[4]={0, 0, -1, 1};
void bfs()
{
	memset(d, -1, sizeof(d));  // 初始化每个点到起点的距离
	q.push({0,0});
	d[0][0] = 0;  // 起点的距离为0
	while (!q.empty())
	{
		int x = q.front().first, y = q.front().second;
		q.pop();
		for (int i=0; i<4; i++)
		{
			int nx = x+dx[i], ny = y+dy[i];
			if (nx<1 || nx>n || ny<1 || ny>n || g[nx][ny]==1 || d[nx][ny]!=-1)
				continue;
			d[nx][ny] = d[x][y]+1;
			q.push({nx, ny});
		}
	}
}
bfs()  // 调用bfs(),dist[i][j]中保存着最短路

多源BFS

// 与单源BFS不一样的地方在与需要将多个值压入队列
void bfs()
{
	// ...
	for (int i=1; i<=cur; i++)
		q.push(a[i]);
	// ...
}

算法应用

题目来源标题难度星级考察算法一句话思路总结
AcWing173 矩阵距离
AcWing175 电路维修
AcWing188 武士风度的牛
AcWing844 走迷宫简单⭐⭐BFS(广度优先搜索)队列从起点 ( 1 , 1 ) (1,1) (1,1) 开始逐层扩展,每次取出队首节点后遍历上下左右四个方向(dx[4]={-1,0,1,0}dy[4]={0,1,0,-1}),若新坐标在边界内、不是障碍、未访问(d[x][y]==-1)则更新距离 d[nx][ny]=d[x][y]+1 并入队;数组模拟队列时 hh 为头指针、tt 为尾指针,d 数组同时作为距离和访问标记(初始化为 -1);BFS 第一次到达终点 ( n , m ) (n,m) (n,m) 时的距离即为最短路径,时间复杂度 O ( n × m ) O(n \times m) O(n×m)
AcWing845 八数码
AcWing1076 迷宫问题
AcWing1096 地牢大师
AcWing1097 池塘计数
AcWing1098 城堡问题
AcWing1099 仙岛求药
AcWing1101 献给阿尔吉侬的花束
AcWing1102 移动骑士
AcWing1106 山谷和山峰
AcWing1107 魔板
AcWing3205 最优配餐
AcWing3675 逃离迷宫
AcWing5977 最少步数
AcWing5978 走迷宫
AcWing5979 走出迷宫
洛谷AT_abc383_c Humidifier 3普及-
洛谷P1162 填图颜色普及-
洛谷P1596 Lake Counting普及-
洛谷[[P1746 离开中山路]]普及-
洛谷[[P2873 Mud Puddles]]普及-
洛谷[[P2958 Papaya Jungle]]普及-
洛谷U483711 最大黑区域普及-
洛谷AT_abc387_d Snaky Walk普及
洛谷AT_abc400_d Takahashi the Wall Breaker普及
洛谷AT_abc405_d Escape Route普及
洛谷AT_abc407_d Domino Covering XOR普及
洛谷AT_abc420_d Toggle Maze普及
洛谷AT_abc425_d Ulam-Warburton Automaton普及
洛谷AT_abc436_d Teleport Maze普及
洛谷P1443 马的遍历普及
洛谷[[P1747 好奇怪的游戏]]普及
洛谷P1825 Corn Maze普及
洛谷P2385 Lilypad Pond普及
洛谷P2895 流星雨普及
洛谷[[P2960 Invasion of the Milkweed]]普及
洛谷[[P7995 Walking Home]]普及
洛谷P8628 穿越雷区普及⭐⭐⭐状态扩展 BFS(三维状态)用三维状态 dist[x][y][last] 表示到达 ( x , y ) (x,y) (x,y) 且上一步经过的格子类型为 last(0 表示 +,1 表示 -)的最短距离;从起点 A A A 出发向四个方向初始化,若邻居是 +dist[nx][ny][0]=1 入队,若是 -dist[nx][ny][1]=1 入队;BFS 扩展时 need = 1-last 表示下一步需要的格子类型,只有当邻居是 need 对应的类型或为终点 B B B 时才可转移,更新 nlast 后入队;最终答案取 dist[edx][edy][0]dist[edx][edy][1] 的最小值,若均不可达输出 -1;时间复杂度 O ( n 2 ) O(n^2) O(n2)
洛谷U203285 找到最大岛普及
洛谷AT_abc460_d Repeatedly Repainting普及+⭐⭐⭐多源 BFS(八方向) + 奇偶性判断先预处理移除内部黑格(八邻域全为黑的格子永远不会变白),然后将所有剩余黑格作为多源起点同时入队 BFS,计算每个位置到最近黑格的最短距离 f[i][j];操作 10 100 10^{100} 10100 次后,最终颜色只与距离奇偶性有关——f[i][j] % 2 == 0 输出 #(黑色),否则输出 .(白色);八方向扩展覆盖上下左右和四个对角线,时间复杂度 O ( H × W ) O(H \times W) O(H×W)
洛谷P1379 八数码难题普及+
洛谷[[P1849 Tractor]]普及+
洛谷P2845 Switching on the Lights普及+
洛谷[[P2919 Guarding the Farm]]普及+
洛谷[[P3456 GRZ-Ridges and Valleys]]普及+
洛谷[[P3663 Why Did the Cow Cross the Road III]]普及+
洛谷P5195 Knights of Ni普及+⭐⭐⭐两次 BFS(多源 BFS)第一次 BFS 从起点出发,计算 dist1(不能经过骑士位置 3);第二次多源 BFS所有骑士位置同时出发,计算 dist2(可以经过任何可通行区域);遍历所有灌木位置 4,取 dist1[r][c] + dist2[r][c] 的最小值作为答案(即起点→灌木→骑士的最短路径);两次 BFS 时间复杂度均为 O ( W × H ) O(W \times H) O(W×H)
洛谷[[P10487 Nightmare II]]普及+
洛谷[[P4667 Switch the Lamp On电路维修]]提高+
AtCoderAT_awc0083_d Escape from the Ice Rink⭐⭐⭐BFS + 滑动扩展(四方向)每次移动选择一个方向后一直滑动到边界或柱子前一个格子才停止,用 BFS 按层遍历求最少移动次数;对于每个出队节点,遍历上下左右四个方向,用 while 循环模拟滑动过程(nx += dx[i], ny += dy[i]),直到超出边界或 mp.count({nx,ny}) 遇到柱子,记录滑动终点;若终点未访问过则更新距离并入队;map<PII,int> 存储柱子位置实现 O ( log ⁡ N ) O(\log N) O(logN) 查询,时间复杂度 O ( H × W × ( H + W ) ) O(H \times W \times (H+W)) O(H×W×(H+W)),若终点不可达输出 `-1$。

【IDA*】

算法模板

算法应用

题目来源标题难度星级考察算法一句话思路总结
AcWing180 排书
AcWing181 回转游戏
洛谷[[P10488 Booksort]]普及+
洛谷P2324 骑士精神提高+
洛谷[[UVA1343 旋转游戏]]提高+
洛谷P2540 斗地主省选

【A*】

算法模板

算法应用

题目来源标题难度星级考察算法一句话思路总结
AcWing178 第K短路
AcWing179 八数码
洛谷P5507 机关提高+
其他[[Remmarguts’ Date]]

【折半搜索】

算法模板

DFS版折半搜索

void dfs(..., int type)  // type用于标记属于前半部分还是后半部分
{
	// 标准dfs过程
	if (...)
	{
		// 达到条件后的处理

		if (type==1) l[sum]++;  // 保存在前半部分
		else r[sum]++;  // 保存在后半部分
		return;
	}
	dfs(..., type);
	dfs(..., type);
}
unordered_map<int, int> l, r;  // 用map记录前半部分和后半部分
dfs(1, mid, 0, 1);  // 搜索前半部分
dfs(mid+1, n, 0, 2);  // 搜索后半部分

枚举版折半搜索

// 枚举前半部分
for (i : 前半部分)
{
	// 将结果存到map中
}
// 枚举后半部分
for (i : 后半部分)
{
	// 得到后半部分结果
	// 计算需要的前半部分的结果
	// 累加组合数(求和的过程)
}

算法应用

题目来源标题难度星级考察算法一句话思路总结
洛谷P4799 世界冰球锦标赛普及+⭐⭐⭐⭐折半搜索(Meet in the Middle) + DFS + 二分查找 N N N 个数分成前后两半,分别用 DFS 枚举所有子集和存入 sasb(剪枝:若剩余最小值超过剩余预算则返回),对两个数组排序后,对于 sb 中每个元素 s s s,用 upper_boundsa 中查找满足 sa[i] <= M - s 的个数并累加,最后加上 sa.size()sb.size()(单一半部分的方案)以及空集;将复杂度从 O ( 2 N ) O(2^N) O(2N) 降为 O ( 2 N / 2 ⋅ N ) O(2^{N/2} \cdot N) O(2N/2N)
洛谷[[P10484 送礼物]]普及+
AtCoderAT_awc0002_e Assortment of Sweets
AtCoderAT_awc0048_e Team Formation⭐⭐⭐⭐折半搜索 + 哈希表计数 N N N 个学生分成前后两半,前半部分用位运算枚举所有子集,用 mp1[cnt][sum] 哈希表记录选择 cnt 人、技能值和模 M M Msum 的方案数;后半部分同样枚举,对于每个子集计算 need_cnt = K - cnt(还需选择的人数)和 need_sum = (M - sum) % M(还需满足的余数),查询 mp1[need_cnt][need_sum] 累加答案;结果对 10 9 + 7 10^9+7 109+7 取模,将复杂度从 O ( C ( N , K ) ) O(C(N,K)) O(C(N,K)) 降为 O ( 2 N / 2 ⋅ N ) O(2^{N/2} \cdot N) O(2N/2N)

【迭代加深】

算法模板

算法应用

题目来源标题难度星级考察算法一句话思路总结
AcWing170 加成序列
洛谷[[UVA529 Addition Chains]]提高+

【舞蹈链】

算法模板

算法应用

题目来源标题难度星级考察算法一句话思路总结
洛谷[[P1784 数独]]普及
洛谷[[P4929 舞蹈链(DLX)]]省选
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值