加载中...
【图论】最近公共祖先LCA
定义什么是最近公共祖先? 在一棵树中(也就是有向无环图中),从根结点遍历到每个结点,路径上所经过的所有结点都叫做该结点的祖先结点 两个结点的祖先结点中重复的部分叫做公共祖先 公共祖先中层数最大的一个点叫做最近公共祖先(LCA) 求法向上标记法要求的两个结点依次向上遍历,找到的第一个公共祖先就是最近公共祖先 但是这个做法比较暴力,时间复杂度O(n) 倍增法首先需要预处理f[i][j]:从结点 i 向上走 2^j^ 步走到的结点,j 的范围是0 <= j <= logn比如说,当 j = 0 时,f[i][j]表示的就是 i 向上走一步到达的结点,其实也就是 i 的父结点 (如果从 i 开始跳 2^j^ 跳出了根结点,那么规定f[i][j] = 0且dist[0] = 0) 怎么实现这个算法呢? 我们发现,从结点 i 向上走 2^j^ 步,相当于结点 i 先向上走 2 ^j-1^ 步,再向上走 2 ^j-1^ 步据此,我们可以利用递归来解决这个问题 用式子可以表示为:f[i][j] = f[f[i][j - 1]][j - 1] 然后,我们需要预处理另一个数组depth[i]: ...
【图论】图的存储与搜索
一、图的存储方式首先,树是一种特殊的图,因此树的存储类似,这里只记录图的存储。 其次,无向图又是一种特殊的有向图。 无向图中的 a—b 相当于有向图中的 a->b b->a,因此这里只记录有向图的存储。 1、邻接矩阵简单来说就是开个二维数组g[N][N]。 无权图中,g就是个bool值,ab之间有边,则g[a][b] = true, 否则为false。 有权图中,g为两条边之间的权重。 出现重边时,邻接矩阵只保留一条边(一般是保留最短的那条)。 空间复杂度O(n^2),经验来看1e5会爆,只适合存储稠密图,稀疏图用邻接表。 2、邻接表(1)链式前向星 h[N]:头结点 e[N]:头结点可以到达哪些点 ne[N]:起到指针的作用 表示e[i]的下一个结点是什么位置 idx:当前存储到了什么位置 链式前向星板子int h[N], e[N], ne[N], w[N], idx;// 首先一定要初始化hmemset(h, -1, sizeof h);void add(int a, int b, int w){ // 加入 a -> b 这条边,权重是w ...
【搜索】双向DFS
双向BFS和前面的双向DFS思路基本一样,都是为了从两端搜索从而避免搜索中间一大块复杂的情况 送礼物原题链接 达达帮翰翰给女生送礼物,翰翰一共准备了 $N$ 个礼物,其中第 i 个礼物的重量是 $G[i]$。 达达的力气很大,他一次可以搬动重量之和不超过 $W$ 的任意多个物品。 达达希望一次搬掉尽量重的一些物品,请你告诉达达在他的力气范围内一次性能搬动的最大重量是多少。 输入格式 第一行两个整数,分别代表 $W$ 和 $N$。 以后 $N$ 行,每行一个正整数表示 $G[i]$。 输出格式 仅一个整数,表示达达在他的力气范围内一次性能搬动的最大重量。 数据范围 $1 ≤ N ≤ 46,$$1 ≤ W , G[i] ≤ 2^{31}−1$ 输入样例20 5754181输出样例19 题意给出背包体积和每个物品体积,问一次最多装多少物品 思路看数据范围判断背包问题一定会TLE,但N范围比较小,因此使用爆搜,把所有可能方案枚举一遍,取一个最大值但是直接爆搜,时间复杂度 2^46^,也是会超时的 优化——双向DFS 用空间换时间先枚举前一半数能凑出来的集合,再枚举后一半数,然后在两个集合中分别 ...
【搜索】DFS迭代加深与IDA*
迭代加深搜索时可能会遇到这样一种情况:明明答案就在第一层!但是因为DFS的缘故浪费很多时间迭代加深就是用来解决这个问题的算法 定义一个 max_depth ,每次搜索时,超过这一层就全部剪掉、(相当于我们划定一个区域,在这个区域内找解,如果找不到,再扩大区域) ?迭代加深和BFS有什么区别呢BFS用队列存储,浪费空间,迭代加深本质是还是DFS,只存储本条路径,还是O(n)的算法 例题加成序列原题链接 满足如下条件的序列 X(序列中元素被标号为 1、2、3…m)被称为“加成序列”: $X[1]=1$ $X[m]=n$ $X[1]<X[2]<…<X[m−1]<X[m]$ 对于每个 k(2 ≤ k ≤ m)都存在两个整数 i 和 j (1 ≤ i , j ≤ k − 1,i 和 j 可相等),使得 $X[k]=X[i]+X[j]$。 你的任务是:给定一个整数 n,找出符合上述条件的长度 m 最小的“加成序列”。 如果有多个满足要求的答案,只需要找出任意一个可行解。 输入格式 输入包含多组测试用例。 每组测试用例占据一行,包含一个整数 n。 当输入为单行的 0 时, ...
【搜索】DFS剪枝与优化
剪枝是什么意思呢? 我们知道,不管是内部搜索还是外部搜索,都可以形成一棵搜索树,如果将搜索树全部遍历一遍,效率会很低,但如果我们能在搜索的过程中,提前预知,判断某一些不可能是正确答案的情况,就可以不用遍历其下的子树,从而提高我们的算法效率 我们可以从以下几个角度考虑剪枝: 优化搜索顺序优先选择分支较少的结点 排除等效冗余尽量保证不搜索重复的状态(就是在不考虑顺序时,采用组合的方式搜索) 可行性剪枝不合法提前退出 最优性剪枝如果当前答案无论如何都比目前的最优解要差,那就可以不要往下搜了 记忆化搜索(DP) 接下来将通过例题来讲解 小猫爬山原题链接 翰翰和达达饲养了 N 只小猫,这天,小猫们要去爬山。 经历了千辛万苦,小猫们终于爬上了山顶,但是疲倦的它们再也不想徒步走下山了(呜咕>_<)。 翰翰和达达只好花钱让它们坐索道下山。 索道上的缆车最大承重量为 W,而 N 只小猫的重量分别是 C1、C2……CN。 当然,每辆缆车上的小猫的重量之和不能超过 W。 每租用一辆缆车,翰翰和达达就要付 1 美元,所以他们想知道,最少需要付多少美元才能把这 N 只小猫都运送下山? 输入格式 ...
【搜索】DFS搜索顺序
马走日原题链接 马在中国象棋以日字形规则移动。 请编写一段程序,给定 n∗m 大小的棋盘,以及马的初始位置 (x,y),要求不能重复经过棋盘上的同一个点,计算马可以有多少途径遍历棋盘上的所有点。 输入格式 第一行为整数 T,表示测试数据组数。 每一组测试数据包含一行,为四个整数,分别为棋盘的大小以及初始位置坐标 n,m,x,y。 输出格式 每组测试数据包含一行,为一个整数,表示马能遍历棋盘的途径总数,若无法遍历棋盘上的所有点则输出 0。 数据范围 1 ≤ T ≤ 9,1 ≤ m , n ≤9,1 ≤ n × m ≤ 28,0 ≤ x ≤ n − 1,0 ≤ y ≤ m − 1 输入样例15 4 0 0输出样例32 题意给出矩阵大小,给出马的初始位置,马只能走日。问有多少种方案让马可以遍历完棋盘上的所有点,每种方案里不可以重复经过两个点 思路这就是典型的外部搜索,是不同状态之间的搜索,因此每次需要恢复现场(可以理解为悔棋) 代码#include <bits/stdc++.h>using namespace std;const int N = 10;int n, m;bool ...
【搜索】DFS连通性模型
DFS 的搜索分为两大部分: 内部搜索:一个图中从一个点搜到另一个点 外部搜索:从一张图(状态)搜到另一张图(状态) 在第一个部分里是图内部点的搜索,每个点只能搜一次,因此搜过的点不需要恢复到原来的(还没被搜过的)状态(意思就是st数组不恢复) 而第二个部分是点的集合之间的搜索,每次搜索完一定要恢复到原有状态才可以进行下一步搜索(意思就是st数组每次需要恢复原状) 迷宫原题链接 一天Extense在森林里探险的时候不小心走入了一个迷宫,迷宫可以看成是由 n∗n 的格点组成,每个格点只有2种状态,.和#,前者表示可以通行后者表示不能通行。 同时当Extense处在某个格点时,他只能移动到东南西北(或者说上下左右)四个方向之一的相邻格点上,Extense想要从点A走到点B,问在不走出迷宫的情况下能不能办到。 如果起点或者终点有一个不能通行(为#),则看成无法办到。 注意:A、B不一定是两个不同的点。 输入格式 第1行是测试数据的组数 k,后面跟着 k 组输入。 每组测试数据的第1行是一个正整数 n,表示迷宫的规模是 n∗n 的。 接下来是一个 n∗n 的矩阵,矩阵中的元素为.或者#。 ...
【搜索】Flood Fill
定义什么是 Flood Fill 算法?字面意思理解:洪水覆盖也就是说,下图的格子分为两大类,一类比较高一类比较低,现在从任意一处较低的格子开始灌水,下一次水将会覆盖它==相邻的==、==较低的==格子,依此类推这就相当于是BFS的思想(也可以用DFS实现,但BFS不会出现爆栈的问题) Flood Fill 算法可以在线性时间复杂度内,找到某个点所在的连通块 例题池塘计数原题链接 农夫约翰有一片 N∗M 的矩形土地。 最近,由于降雨的原因,部分土地被水淹没了。 现在用一个字符矩阵来表示他的土地。 每个单元格内,如果包含雨水,则用”W”表示,如果不含雨水,则用”.”表示。 现在,约翰想知道他的土地中形成了多少片池塘。 每组相连的积水单元格集合可以看作是一片池塘。 每个单元格视为与其上、下、左、右、左上、右上、左下、右下八个邻近单元格相连。 请你输出共有多少片池塘,即矩阵中共有多少片相连的”W”块。 输入格式 第一行包含两个整数 N 和 M。 接下来 N 行,每行包含 M 个字符,字符为”W”或”.”,用以表示矩形土地的积水状况,字符之间没有空格。 输出格式 输出一个整数,表示池塘数目。 ...
【搜索】BFS中的最短路模型
BFS可以解决边权为1的最短路问题,下面是相关例题 单源最短路将源点在开始时存进队列 迷宫问题原题链接 给定一个 n×n 的二维数组,如下所示:int maze[5][5] = {0, 1, 0, 0, 0,0, 1, 0, 1, 0,0, 0, 0, 0, 0,0, 1, 1, 1, 0,0, 0, 0, 1, 0,};它表示一个迷宫,其中的1表示墙壁,0表示可以走的路,只能横着走或竖着走,不能斜着走,要求编程序找出从左上角到右下角的最短路线。 数据保证至少存在一条从左上角走到右下角的路径。 输入格式 第一行包含整数 n。 接下来 n 行,每行包含 n 个整数 0 或 1,表示迷宫。 输出格式 输出从左上角到右下角的最短路线,如果答案不唯一,输出任意一条路径均可。 按顺序,每行输出一个路径中经过的单元格的坐标,左上角坐标为 (0,0),右下角坐标为 (n−1,n−1)。 数据范围 0 ≤ n ≤ 1000 输入样例50 1 0 0 00 1 0 1 00 0 0 0 00 1 1 1 00 0 0 1 0输出样例0 01 02 02 12 22 32 43 44 ...
【搜索】最小步数(双向广搜与A*算法)
最小步数魔板原题链接 Rubik 先生在发明了风靡全球的魔方之后,又发明了它的二维版本——魔板。 这是一张有 8 个大小相同的格子的魔板:1 2 3 48 7 6 5我们知道魔板的每一个方格都有一种颜色。 这 8 种颜色用前 8 个正整数来表示。 可以用颜色的序列来表示一种魔板状态,规定从魔板的左上角开始,沿顺时针方向依次取出整数,构成一个颜色序列。 对于上图的魔板状态,我们用序列 (1,2,3,4,5,6,7,8) 来表示,这是基本状态。 这里提供三种基本操作,分别用大写字母 A,B,C 来表示(可以通过这些操作改变魔板的状态): A:交换上下两行;B:将最右边的一列插入到最左边;C:魔板中央对的4个数作顺时针旋转。 下面是对基本状态进行操作的示范: A:8 7 6 51 2 3 4B:4 1 2 35 8 7 6C:1 7 2 48 6 3 5对于每种可能的状态,这三种基本操作都可以使用。 你要编程计算用最少的基本操作完成基本状态到特殊状态的转换,输出基本操作序列。 注意:数据保证一定有解。 输入格式 输入仅一行,包括 8 个整数,用空格分开,表示目标状态。 输出格式 输出文件的第 ...