- 博客(74)
- 收藏
- 关注
原创 【C++】二分答案——生产零件
对于普通的查找,我们需要遍历整个序列,时间复杂度为O(n)。当序列有序且可以随机访问时(有序的数组),这时候我们就可以使用二分来进行查找,时间复杂度将缩减为O(logn)。:返回结果为 true/false;:返回结果为第一个大于等于某数的地址(切记不是数组的索引);:返回结果为第一个大于某数的地址(切记不是数组的索引);注意 set 里面也有二分查找(set st;:在集合中找大于等于 xx 的最小数,返回迭代器;:在集合中找大于 xx 的最小数,返回迭代器;
2022-10-16 21:15:37 580 1
原创 链表——一题弄懂
idx[i]表示能力为i的人的所在位置,vis[i]表示位置i的人所在的队伍,m[i].pre和m[i].nxt分别为上一个结点的位置和下一个结点的位置。
2022-10-11 22:12:16 191
原创 【C++】栈的运用——卡特兰数
1,2,3,4依次入栈,可能的出栈序列有多少种?我们来模拟一下就知道了。考虑4最先出栈,那么就只有4321这一种情况。考虑3最先出栈,那么 44 还没有入栈,栈中有1,2,3那么出栈顺序有3214,3241,3421三种情况。考虑2最先出栈,那么有2134,2143,2314,2341,2431五种情况。考虑1最先出栈,那么有1234,1243,1324,1342,1432五种情况。总共有1+3+5+5。
2022-10-11 21:59:28 1164
原创 [NOIP2017]棋盘 深度优先搜索
如果当前需要花费的价值超过当前结点最少需要花费的钱,那么就直接停止搜索,否则把当前结点的最小价值更新。时,上一次是无色的即下一次不可以再走无色的了,否则是有色的即下一次可以走无色的。先初始化图中所有点的最低成本为0x3f3f3f3f。我在存图中,记录无色为0,红色为1,黄色为2。分别遍历当前结点上下左右可以走到的节点。
2022-10-06 09:09:18 230
原创 [NOIP2014]寻找道路与[NOIP2009]最优贸易 搜索
挨个遍历u能到达的结点,其中如果vis[v]的这个条件其实可有可无,因为前面都判断了u的所有子节点包括v都符合不需要可行性剪枝了。每当找到一个节点,如果当前结点为终点,则当前就是最短路,u=t时必然满足条件1,因为任何一个它的边,都能直接到达终点t。最优性剪枝:如果当前访问过,且有花最少的钱买到水晶球走到现在这一个节点的结果,直接将当前位置的最优答案更新。表示要买u前面的最便宜的地方的在后续卖钱,其中&val[u]$表示要买u编号的买在后面卖钱。挨个查找终点能直接或间接到达的编号,其中如果编号为。
2022-10-05 19:58:16 406
原创 [C++]线性模型和递推
定义f[i]为结尾为第i个元素的最大子段和f[i]=max(f[i−1]+a[i],a[i])边界条件为f[0]=0最终答案为f[n]也可以转换成前缀和,求出前缀和和前面的前缀和的最小值做差。
2022-10-05 13:37:56 218
原创 [C++]背包问题
总共n个商品可选择,每个商品你最多只能选择一个,第i个商品的体积为wi,价值为vi;背包的最大容积为V。问商品总体积不超过V所能得到的最大值。f[i][j]表示前i个物品体积之和不超过j的最大价值。f[i][j]=max(f[i−1][j−w[i]]+v[i],f[i−1][j])初始化f[0][0]=0答案为f[n][V]
2022-10-05 12:05:19 684 1
原创 【C++】Floyd多源最短路算法
多源是指它可以求出以每个点为起点到其他每个点的最短路不过其实有一种情况求不出最短路,就是有负环的情况,此时可以不断地在环中转圈,而Floyd算法无法判断这样的情况,所以就只能在没有负环的情况下使用。Floyd算法是一种利用动态规划的思想,计算给定的带权图中任意两个顶点之间的最短路径的算法,无权图可以把每个边的边权看作1.我们用dp[k][i][j]dp[k][i][j]dp[k][i][j]表示iii到jjj能经过111~kkk的点的最短路,那么实际上dp[0][i][j]dp[0][i][j]dp[0
2022-07-05 15:18:23 395
原创 【C++】二叉堆和优先队列
堆(Heap)是计算机科学中一类特殊的数据结构,通常是一棵完全二叉树(每个结点只有不超过两个子结点,只有最下边一层从右开始不缺少或连续缺少一段结点,其它层都是满的的树形结构)。堆的性质有:将根结点最大的堆叫做大根堆,根结点最小的堆叫做小根堆。常见的堆有二叉堆、斐波那契堆等。左是小根堆,右是大根堆:堆的定义如下:nnn 个元素的序列 A1,A2……Ai{A_1,A_2……A_i}A1,A2……Ai当且仅当满足下关系时,称之为堆。Ai≤A2i,Ai≤A2i+1A_i\leq A_{2i},A_i\
2022-07-05 15:03:54 338
原创 【C++】根据遍历确定二叉树
已知中序遍历和先序遍历可以唯一确定一棵二叉树,因为根结点是先序遍历的第一个结点,我们只要在中序遍历中找到根结点的位置,就能划分左右子树。然后递归地处理左右子树,就能唯一构建一棵二叉树。同理已知中序遍历和后序遍历也能唯一确定一棵二叉树。但是已知先序遍历和后序遍历不能唯一确定一棵二叉树,反例如下:左右两棵树的先序遍历都为,后序遍历都为,但不相同。已知先序遍历和中序遍历求二叉树的方法如下设先序遍历71432657 1 4 3 2 6 57143265中序遍历为13427561 3 4 2 7 5
2022-07-05 14:31:30 470
原创 【C++】二叉树
在计算机科学中,二叉树是每个节点最多有两个子树的树结构。二叉树可以为空树,通常子树被称为”左子树“和”右子树“比如下图就是一棵二叉树二叉树的每个节点最多有两棵子树(不存在度大于2的节点),二叉树的子树有左右之分,次序不能颠倒二叉树的第i层,最多有2i−12^{i-1}2i−1个结点深度为k的二叉树,最多有2k−12^k-12k−1个结点对于任何一颗二叉树,如果其叶结点数为n0n0n0,度为2的节点数为n2n2n2,则n0=n2+1n0=n2+1n0=n2+1对于最后一个结论可以这么理解,当我们
2022-07-04 14:59:29 487
原创 动态规划——区间DP
区间DP:指在一段区间上进行的一系列动态规划对于区间DP这一类问题,我们需要计算区间[1,n][1,n][1,n]的答案,通常用一个二维数组dpdpdp表示,其中dp[x][y]dp[x][y]dp[x][y]表示区间[x,y][x,y][x,y]有些题目,dp[l][r]dp[l][r]dp[l][r]由dp[l][r−1]dp[l][r-1]dp[l][r−1]与dp[l+1][r]dp[l+1][r]dp[l+1][r]退到;也有题目,我们需要枚举[l,r][l,r][l,r]内的中间点,由两个子
2022-06-23 18:03:12 252
原创 DAG(有向无环图)上的动态规划
动态规划最重要的是要找到一个情况的所有子情况,处理完所有子情况后把结果通过一定的方式转移得到这个情况的答案。那么很多时候我们会遇到或者可以构造出这样一个图,每个点是一种情况,每条边代表着这条边的终点是起点的一个子情况。首先这个图是有向的,如果他里面没有环,我们称之为有向无环图(DAG),我们可以在这个图上通过状态转移的到最后的答案。接下来我们考虑如何进行动态规划的状态转移。其实,想到一个合理的转移顺序不太容易,不妨尝试用记忆化搜索的方法来做,因为是无环的,每条路径一定会到达一个终点,不会陷入无尽的环
2022-06-22 17:35:40 579
原创 邻接表的链表实现——链式前向星
只是先邻接表时,我们使用了vector来存储每个顶点连出的边。但是由于vector的常数很大,对于某些时间限制较为严格的情况,使用vector存储邻接表会导致程序超时。因此,我们需要一种更高效的存储方式——链式前向星链表一般会有一个结构体表示一个节点,然后节点之间首尾相接的结构就叫链表。这种只包含下一个元素,对于查找上一个元素需要将整个链表再遍历一遍,为了使用方便,也有双向或者循环链表的说法,就可以不单找到它的下一个节点,也可以找到它前一个节点。基于链表的邻接表实现v表示这条边的终点next表示这条
2022-06-22 17:25:09 308
原创 记忆化搜索
记忆化搜索是一种搜索的形式,对搜索的结果用数组或其他数据结构记录下来。若当前状态搜索过了,则返回已存储的答案。这样,每个状态最多计算1次。我们以斐波那契数列为例,用递归实现的fib数组计算代码是这样的:搜索树是长这样的我们可以发现,为了求Fib(5)Fib(5)Fib(5),会先求Fib(4)Fib(4)Fib(4),然后求出Fib(3)Fib(3)Fib(3).在求Fib(4)Fib(4)Fib(4)的时候,实际上已经把Fib(3)Fib(3)Fib(3)求出来了,而求Fib(5)Fib(5)Fi
2022-06-22 17:01:34 4019 2
原创 状压DP——子集DP
我们直接通过一个例题来了解子集 DP,子集 DP 属于状压 DP 的一种。给定一个长度不超过 n 的字符串 s,如果 s 中的一个子序列是回文,那么我们就可以从 s 中移除这个子序列,求最少经过多少步我们可以移除整个字符串 s。如:我们可以从"dqewfretd"中移除 “defed”,剩下的字符串即:“qwrt”。使用状压DP进行求解:对于状态i,用dp[i]dp[i]dp[i]表示最少的操作次数当状态i对应的子序列是回文时,dp[i]=1对于状态i的一个子状态t,如果t也是回文序列,那么dp[
2022-06-20 16:58:27 715
原创 状态压缩动态规划进阶——方格取数问题
给定一个 n×mn \times mn×m 的矩阵,行数和列数都不超过 20,其中有些格子可以选,有些格子不能选。现在你需要从中选出尽可能多的格子,且保证选出的所有格子之间不相邻(没有公共边)。例如下面这个矩阵(2×32 \times 32×3的矩阵)最多可选 33 个互不相邻的格子,方案如下(选中的位置标记为x):解法详解我们可以自上而下一行一行选择格子。在选择格子的过程中,只和上一行选择的方案有关,所以我们可以将“当前放到第几行,当前行的选择方案”作为状态进行状态压缩动态规划。一行里被选择的格子
2022-06-19 16:41:07 227
原创 状态压缩动态规划进阶——旅行商TSP问题
旅行商问题,即 TSP 问题(Traveling Salesman Problem)又译为旅行推销员问题、货郎担问题,是数学领域中著名问题之一。假设有一个旅行商人要拜访 n 个城市,他必须选择所要走的路径,路径的限制是每个城市只能拜访一次,而且最后要回到原来出发的城市。路径的选择目标是要求得的路径路程为所有路径之中的最小值。TSP 是一道经典的 NP-完全问题,在规模比较小的时候可以用动态规划求解。有 nnn个城市,两两之间均有道路直接相连。给出每个城市 iii 和 jjj 之间的道路长度 dist(i,j
2022-06-19 16:02:49 1214 1
原创 状态压缩动态规划基础——传递物品
个人在做传递物品的游戏,编号为 1-n。游戏规则是这样的:开始时物品可以在任意一人手上,他可把物品传递给其他人中的任意一位;下一个人可以传递给未接过物品的任意一人。即物品只能经过同一个人一次,而且每次传递过程都有一个代价;不同的人传给不同的人的代价值之间没有联系。求当物品经过所有 nn个人后,整个过程的最小总代价是多少。我们很容易能够想到一个动态规划解法,状态为是否给过某个人以及现在在谁的手上,为了方便记录,数组下标从0开始,即当n=3时,就可以用dp[[2][2][2][3]dp[[2][2][2]
2022-06-19 15:45:09 152
原创 状态压缩搜索
前置知识,需要会位运算基本技巧位运算基本技巧在进行抽象搜索的时候,我们要研究状态的表示 ,有时就需要状态压缩的二进制数表示状态或者表示状态的某一维,例如有若干盏灯,每盏灯是亮的或者暗的,有一些开关,每个开关可以让一些灯改变状态,初始情况是所有灯都亮着,问最少需要多少次按开关的操作才能把所有灯变暗。对于这个问题,物品,我们就可以用一个二进制数来表示每盏灯的情况,这个二进制数这一位为0,代表相应的一盏灯两者,为1代表相应的一盏灯暗着,这样二进制数也就相等于这些灯的哪个子集中的灯是暗着的。我们以所有灯都亮
2022-06-18 08:34:41 230
原创 位运算技巧
看这个内容,需要有的前置知识:二进制简介位运算简介位运算应用我们之前学习了基础的位运算,不过有时我们需要进行更复杂的运算表示我们想要表达的意思,那么我们来看一看位运算有哪些性质,有哪些我们常见的使用位运算的技巧。.........
2022-06-17 08:23:49 135
原创 二进制的应用——枚举子集
集合是指由一个或多个确定的元素所构成的整体,也可以当作不分顺序的数组当集合中不包含任何元素时,我们称它为空集我们一般用大括号及期中若干元素表示一个集合,例如1,3,5{1,3,5}1,3,5表示包含元素1,3,5的一个集合,a,x,abc{a,x,abc}a,x,abc表示包含三个字符串元素的集合在集合的元素中没有先后顺序,例如集合1,2,3{1,2,3}1,2,3和{3,1,2}是等价的在集合中,有一些集合间的关系,我们这种送会用到一个关系是子集,我们说集合A是集合B的子集,表示A钟所有元素都在B
2022-06-17 08:08:18 815
原创 二进制与位运算
代码中对于二进制的处理可以用位运算来实现,位运算是对二进制的每一位进行计算,所以每一位都只有0和1两种可能。先介绍三种常用的位运算符号,与&,或|,异或^,运算符和规则如下图所示或|在表格中无法显示,应该是A|B在信息学中,一定要注意,不要把A ^ B当成A的B次方(在信息学中,当然在数学中不要把A^B当成A异或B)位运算中有两种操作,左移。右移具体还分为带符号右移和无符号右移,我们提到的是带符号右移,无符号右移使用比较少,这里不再做解释。对于A...
2022-06-05 20:51:59 494
原创 二进制数与二进制、十进制互化
在数学和数字电路中,**二进制(binary)**数是指用二进制计数系统,即以2为基数的计数系统表示的数字。这一系统中,通常用两个不同的符号0(代表零)和1(代表一)来表示。以2为基数代表系统是二进位制的。数字电路中,逻辑门的实现直接应用了二进制,因此现代计算机都是二进制。每个数字成为一个位元(二进制位)或比特(Bit,Binary digit的缩写)。整数部分,把十进制转化二进制一直分解至商数为0.读余数从下读到上,即使二进制的整数部分。小数部分,先将其×2,取其整数部分的结构,再用计算后的小数部分依次
2022-06-05 20:16:52 635
原创 双向广度优先搜索bfs
还有一类广度优先搜索的题目会在数字的扩展和搜索上,我们假设需要从起始状态到达目标状态需要40步,每次扩展最多产生2种状态,那么最差情况下,状态数量会到达2402^{40}240。很明显,如果我们不使用一些措施,我们的队列中必然无法存储如此多的情况,最终导致空间超限。有没有什么好的解决办法呢?其实很容易能想到,我们可以从起始状态和目标状态一起开始搜索,类似于多起点BFS。一般我们有两种方案完成双向广度优先搜索,一种类似于多起点BFS,但是在记录状态时将其分开,一旦从一端搜到已经被另一端访问过的元素,那么我
2022-05-30 22:32:31 282
原创 BFS中需要表示的状态
在一些问题中,我们会较难地对状态表示,此时,我们需要进行决策(或者说可以移动的动作)结合在一起。状态和决策互相配合,一旦在某个状态下有合法的决策,那么状态就必然发生变化。以《华容道》一题为例,其具体操作规则为:在一个n×m 棋盘上有n×m 个格子,其中有且只有一个格子是空白的,其余 n \times m-1n×m−1 个格子上每个格子上有一个棋子,每个棋子的大小都是 1×1 的;有些棋子是固定的,有些棋子则是可以移动的;任何与空白的格子相邻(有公共的边)的格子上的棋子都可以移动到空白格子上。游戏的
2022-05-28 08:32:28 183
原创 bfs广度优先搜索的标记策略与康托展开
在之前的问题中,将已到达的状态使用布尔数组或整数数组进行标记,从而避免重复访问以及得到最小步数等。当一道题的数据范围过大,例如vis数组已经需要开到甚至10^9时,为解决这样的情况带来的问题。一个很显然的方案,可以通过set或map数据结构来标记当前已经访问的位置。对于上面的问题,直接创建map<int,int> mp来标记访问的数字和对应的最小步数。另外在一些时候,需要创建结构体放入set或map中,然而直接将自建类型作为该数据结构的类型是会报错的。这是由于set和map都是基于二叉排序
2022-05-28 07:39:28 195
原创 广度优先搜索——动态类迷宫问题
动态类迷宫中通常会出现其中一些特殊的物品,按一定周期进行变化。我们假设迷宫中有一个楼梯,且是动态的,每隔一个单位时间,楼梯就会变换一次方向。楼梯的形状在开始时为 ‘-’ 或 ‘|’,表示其连接横向上的两个格子或纵向上的两个格子。 而当梯子转到另一方向(纵向)时,垂直于梯子方向(横向)是无法通行的。在此问题中允许蒜头君停留在某个除梯子的可行位子。动态迷宫的具体输入为第一行 n 和 m 表示迷宫的行数和列数。接下来是一个 N 行 M 列的地图,‘*‘表示障碍物,’.‘表示走廊,’|’ 或者 ‘-’ 表示一个
2022-05-27 22:24:52 361
原创 广度优先搜索中的状态表示——钥匙型迷宫问题
显然,保存状态、更新状态是广度优先搜索问题的核心,在不同类型的问题中,需要记录的信息是不相同的。本篇文章,我们将从钥匙迷宫问题、动态迷宫问题这几个例子出发,探究具体需要表示的状态,并对较多的状态进行建模。钥匙型迷宫顾名思义,钥匙型迷宫就是在普通的迷宫问题上,加入钥匙的限制,并在此基础上引申些其他概念,如钥匙能够开门。使得门所在的格子变为可通行的。如果只要拿到任意一个把钥匙即可,我们可以巧妙地用多起点bfs来解决。那么在钥匙数量更多、钥匙有多种类型地情况我们应该如何对问题建模,钥匙作为一种状态应当如何
2022-05-27 18:20:56 319
原创 用广度优先搜索探索迷宫问题
前面我们已经学会了如何用dfs解决迷宫最短路问题。用dfs求解迷宫有一个很大的缺点,需要枚举所有可能的路径,读入的地图一旦很大,可能的搜索方案数量会非常多,用dfs搜索显然效率非常低。我们可以借助bfs来求解迷宫问题。由于bfs时分层搜索,因此,第一次搜索到终点的时候,当前搜索的层数就是最短路径的长度。...
2022-05-23 19:28:14 511
原创 广度优先搜索bfs
广度优先搜索,又称宽度优先搜索,简称bfs,我们用bfs表示广度优先搜索。与深度优先搜索不同的是,广度优先搜索会先将与起始点距离较近的点搜索完毕,再搜索更远的点,二深搜却是沿着一个分支搜到最后(不撞南墙不回头)。bfs从起点开始,优先搜索离起点最近的点,然后由这个点最近的点扩展到其他稍近的点,这样一层一层扩展,就像水波扩散一样。对上图进行深度优先搜索访问的顺序的序列:A->B->E->F->C->D->G对上图进行广度优先搜索访问的顺序的序列:A->B-&g
2022-05-23 19:00:44 2090
原创 深度优先搜索dfs剪枝例题讲解
引爆炸弹在一个 n×m 的方格地图上,某些方格上放置着炸弹。手动引爆一个炸弹以后,炸弹会把炸弹所在的行和列上的所有炸弹引爆,被引爆的炸弹又能引爆其他炸弹,这样连锁下去。现在为了引爆地图上的所有炸弹,需要手动引爆其中一些炸弹,为了把危险程度降到最低,请算出最少手动引爆多少个炸弹可以把地图上的所有炸弹引爆。数据范围:1≤n,m≤1000;这问题其实与连通块类似。我们把可以互相引爆的炸弹放到同一个集合中,那么最后划分的集合个数就是我们要求的答案。显然,引爆集合中任意一个炸弹造成的效果都一样,我们可以遍
2022-05-22 08:59:44 401
原创 深度优先搜索奇偶性剪枝
我们先来看一道题目:有一个n×m 大小的迷宫。其中字符’S’表示起点,字符’D’表示出口,字符’X’表示墙壁,字符’.‘表示平地。你需要从’S’出发走到’D’,每次只能向上下左右相邻的位置移动,并且不能走出地图,也不能走进墙壁。每次移动消耗 1 时间,走过路都会塌陷,因此不能走回头路或者原地不动。现在已知出口的大门会在 T 时间打开,判断在 0 时间从起点出发能否逃离迷宫。数据范围 n,m≤10,T≤50。只有从起点往终点搜,步数恰好等于T时,才能成立。我们只需要DFS来搜索每条路线,并且只需要搜
2022-05-21 22:20:52 373
原创 深度优先搜索重复性剪枝
对于某些特定的搜索方式,一个方案可能被搜索多次,这样是没有必要的。给定 n 个整数,要求选出 K 个数,使得选出来的 K 个数的和为 sum。(我直接给了一组样例)没剪枝之前的dfs代码示例:#include <iostream>using namespace std;int n, k, sum, ans;int a[40];bool xuan[40];void dfs(int s, int cnt) { for (int i = 0; i < n; i++) {
2022-05-20 08:08:00 244
原创 深度优先搜索最优性剪枝
对于求最优解的一类问题,通常可以用可行性剪枝,比如再求解迷宫最短路的时候,如果发现当前的步数已经超过了当前的最优解,那么当前状态开始的搜索都是多余的,因为这样搜索下去永远都不可能搜到更优解。通过这样的剪枝,可以省去大量多余的计算。此外,在搜索是否有可行解的过程中,一旦找到了一组可行解,后面的搜索都不必再进行了,这算是最优性剪枝的一个特例。有一个 n×m 大小的迷宫。其中字符’S’表示起点,字符’T’表示终点,字符’*‘表示墙壁,字符’.‘表示平地。你需要从’S’出发走到’T’,每次只能向上下左右相邻的位
2022-05-20 07:57:29 576
原创 深度优先搜索可行性剪枝
我在前面的文章中提到过搜索过程中最后会生成一棵搜索树。剪枝,顾名思义,通过一些判断,砍掉搜索树上不必要递归的子树。有时候,我们会发现某个结点对应子树的状态都不是我们要的结果,那么我们其实没必要对这个分支进行搜索,砍掉这个子树,就是剪枝。剪枝其实是和边界条件类似,边界条件本质也应该是剪枝。回顾一下之前讨论过的问题:给定n个整数,要求选出K个数,使得选出的K个数的和为sum。(我在代码中已经提供了样例,用于测试dfs速度)没剪枝的代码:#include <iostream>using
2022-05-20 07:31:47 286
空空如也
空空如也
TA创建的收藏夹 TA关注的收藏夹
TA关注的人