
算法(学习)
文章平均质量分 88
算法(学习)笔记。
大桔骑士v
微软程序员,B站账号:大桔骑士v
展开
-
【算法学习笔记】37:扩展中国剩余定理(EXCRT)求解任意线性同余方程组
中国剩余定理仅适用于模数m1...mn两两互质的情况,如果没有这个限制条件则不能使用。下面进行一般情况的解法推导,注意虽然叫扩展中国剩余定理(EXCRT),实际上只是扩宽了中国剩余定理(CRT)的求解限制,解法和原理上和CRT完全没啥关系。原创 2025-04-14 00:21:23 · 607 阅读 · 0 评论 -
【算法学习笔记】36:中国剩余定理(CRT)求解模数互质的线性同余方程组
假定存在m1..mk两两互质,中国剩余定理旨在求解这样的线性同余方程组中的xx≡a1modm1x≡a2modm2...x≡akmodmkMi1∏kmiMimiM易知Mi和mi互质。记Mi−1表示Mi模mi的逆,则定理给出的一个xxi1∑kai⋅Mi⋅Mi−1。原创 2025-01-27 21:08:14 · 1059 阅读 · 0 评论 -
【算法学习笔记】35:扩展欧几里得算法(EXGCD)求解线性同余方程
线程同余方程问题是指ax≡bmodm,给定ab和m,找到一个整数x使得该方程成立,即使得axmodmb,随便返回任何一个解都可以。例如4x≡3mod5,那么x的一个可能的解可以是2。接下来用扩展欧几里得算法尝试构造这个解。从ax≡bmodm可知,一定存在一个ya⋅xm⋅yb也就是说,因为ax模m的余数是b,所以ax一定可以表示成m的整数y倍再加上一个b。ax−myb令。原创 2025-01-21 00:07:24 · 1108 阅读 · 0 评论 -
【算法学习笔记】34:扩展欧几里得算法(EXGCD)
对于任意正整数ab,一定存在整数系数xyaxbygcdab并且gcdab是对于任意的系数x和y放在a和b上能凑出的最小正整数。原创 2025-01-20 21:30:17 · 865 阅读 · 0 评论 -
【算法学习笔记】33:快速幂的递归及循环实现
要计算ababmodp,可以考虑用折半的方式缩小计算量。例如要计算213,只要计算26乘以自己,再乘以一个多出来的2。而要计算26,只要计算23乘以自己。而要计算23,只要计算21乘以自己,再乘以一个多出来的2。也就是每次把b折半下取整,当baba2b⋅a2b当b是奇数时只要多乘以一个aaba2b⋅a2b⋅a如果需要对p取模,只要在每一步乘法之后取模就可以了。原创 2025-01-18 00:15:17 · 1141 阅读 · 0 评论 -
【算法学习笔记】32:筛法求解欧拉函数
上节学习的是求一个数n的欧拉函数,因为用的试除法,所以时间复杂度是On,如果要求m个数的欧拉函数,那么就会花Omn的时间。如果是求连续一批数的欧拉函数,可以用筛法进行优化。原创 2025-01-15 21:54:42 · 1123 阅读 · 0 评论 -
【算法学习笔记】31:试除法分解质因数及求解欧拉函数
欧拉函数ϕn\phi(n)ϕn表示从1到n中和n互质的数字的个数,两个数字除了1没有其它公因数就是互质。比如从1到6之间和6互质的数字只有1和5,所以phy62phy(6) = 2phy62。设nnnnp1α1p2α2pkαknp1α1p2α2..pkαkϕnn⋅1−1p1⋅1−1p2⋅⋅1−1pkϕnn⋅1−p11⋅。原创 2025-01-15 01:27:25 · 877 阅读 · 0 评论 -
【算法学习笔记】30:埃氏筛(Sieve of Eratosthenes)和线性筛(Linear Sieve)
也叫欧拉筛,在埃氏筛的思想下,想办法让每个合数只被筛出去一次,消除重复筛选,这样时间复杂度就能降低到。写法上应该注意,线性筛不是像埃氏筛那样,只在发现质数的轮次筛合数,而是在每个轮次。只被筛出去一次,由于我们是从小到大筛选质数的,因此可以考虑让这个合数。的因数就可以了,因为我们是从小到大遍历质数。,不论是质数或者合数,其最小质因数如果是。的倍数都筛掉,存在重复筛选,时间复杂度。的质数都已经被筛好了,我们可以对于每个。的最小质因数,这时候就可以停止筛了。,如果没有停下来,又取了下一个质数。原创 2025-01-13 23:16:33 · 929 阅读 · 0 评论 -
【算法学习笔记】29:动态规划中可丢弃状态的维度压缩
当状态i只依赖于前置状态i−1,并且在计算出状态i之后就可以丢弃状态i−1时的解时,i−1就成为一个可丢弃的状态,因此就可以将i这个维度直接压缩(省略)掉,用一个变量不停的更新自己就可以了,可以直接节省一个维度的空间占用。原创 2024-08-25 15:59:03 · 1170 阅读 · 0 评论 -
【算法学习笔记】28:Meet In The Middle优化
当一个问题使用时间复杂度为Oexprn))会超时或者爆内存时,如果它存在这样的性质,可以分别对折半后的前后两个2n的子问题进行搜索,并能根据这两个子问题的解,在Oexprn))的时间内得到原问题的解时,就可以分别处理两个折半后的子问题,然后用两个子问题计算原问题的结果,称为Meet In The Middle。这样就把时间复杂度从Oexprn))变成了O2⋅expr2n))原创 2024-08-25 01:25:21 · 1322 阅读 · 0 评论 -
【算法学习笔记】27:区间DP
1 区间DP区间DP指的是状态表示是某个区间的DP问题。在区间DP里循环的顺序不太好写,因为要在计算时确保所需要的状态都计算过了,顺序一般可以先按区间长度从小到大来枚举。2 模板题:石子合并分析如下图。只要考虑最后一步,合并的是左右的哪两个区间,也就是枚举分界点kkk,最后一步合并的就是区间[i,k][i, k][i,k]和[k+1,r][k + 1, r][k+1,r]。不管分界点kkk怎么选,合并代价都是区间[i,j][i, j][i,j]的区间和,这里用前缀和来维护,所以就是s[j]−s[原创 2021-02-17 17:27:48 · 590 阅读 · 0 评论 -
【算法学习笔记】26:匈牙利算法(二分图最大匹配)
1 简述给定一个二分图,例如:匈牙利算法能够快速的计算出一种匹配方式,使得匹配的数量最多。注意,一个成功的匹配方式中,没有两条边是共用了同一个点的。形象的说,这个问题可以理解成二分图两边分别是男生和女生,有连线的表示可以凑成一对,匈牙利算法就是用来计算最多能够凑成多少对(不存在脚踏多条船的情况)。例如左边是男生,右边是女生,可以任选一方为主动方,比如是男生方,那么流程如下。对于每个男生结点,对所有与之有连线的女生结点,检查对应的女生是不是单身,如果是就直接凑成一对。那么在上图的例子中,前两个男原创 2021-02-16 16:07:30 · 511 阅读 · 0 评论 -
【算法学习笔记】25:染色法(判定二分图)
1 简述要判断一个图是不是二分图,可以用两种颜色对图上的所有结点进行染色。染色法的基本思路是,如果一个图是二分图,那么一个结点所连接的所有结点都一定和这个结点属于不同侧,也就应该染上不同的颜色。所以只要先将一个结点染成颜色111,然后检查它的所有邻接结点jjj。如果jjj已经染过颜色了,而且和本结点的颜色一样,那么就发生了冲突,说明不能形成二分图。如果jjj结点没有染过颜色,就将其染成和自己这个结点颜色不一样的(比如自己是111,那么就把jjj结点染色成222),这是一个递归的过程,如果这个过程原创 2021-02-14 00:24:59 · 2360 阅读 · 0 评论 -
【算法学习笔记】24:Prim算法与Kruskal算法(最小生成树)
Prim算法和Dijkstra算法很相似,而且也按照是不是稀疏图分成了两种:对于稠密图,用朴素版的Prim算法,时间复杂度O(n2)O(n^2)O(n2)对于稀疏图,用堆优化版的Prim算法,时间复杂度O(mlogn)O(mlogn)O(mlogn)Kruskal算法的时间瓶颈在于它需要对所有边从小到大排序,所以时间复杂度是O(mlogm)O(mlogm)O(mlogm),由于m<n2m < n^2m<n2,所以mlogm<mlog(n2)=2mlognmlogm <原创 2021-02-13 00:15:33 · 1258 阅读 · 0 评论 -
【算法学习笔记】23:Floyd算法(多源汇最短路)
1 简述Floyd算法用于求多源汇最短路,时间复杂度O(n^3)。首先用邻接矩阵里的d[i,j]d[i, j]d[i,j]存储所有的边(重边的时候取minminmin),然后就是三重循环,思路也是如果从iii到kkk,再从kkk到jjj,这个距离能比d[i,j]d[i, j]d[i,j]小,就更新一下:d[i,j]=min(d[i,j],d[i,k]+d[k,j])d[i, j] = min(d[i, j], d[i, k] + d[k, j])d[i,j]=min(d[i,j原创 2021-02-10 14:23:10 · 402 阅读 · 0 评论 -
【算法学习笔记】22:SPFA算法(带负权边单源点最短路、检测负权回路)
1 简述SPFA算法是对Bellman-Ford算法的优化,也是用于在存在负权边的图上,求单源点最短路,一般情况下时间复杂度可以看作O(m)O(m)O(m)的,最坏情况下时间复杂度是O(nm)O(nm)O(nm)。虽然SPFA算法是对Bellman-Ford算法的优化,但是不是所有用Bellman-Ford算法的问题都能用SPFA来代替。例如,对最短路经过的边数做一个限制,要求经过的边数≤k\leq k≤k的最短路,这个时候能用Bellman-Ford算法,但是不能用SPFA算法来做。SPFA算法是单原创 2021-02-10 00:21:12 · 2004 阅读 · 0 评论 -
【算法学习笔记】21:Bellman-Ford算法(有边数限制的单源点最短路)
1 应用:计算有边数限制的单源点最短路Bellman-Ford算法用于在存在负权边的图上,求单源点最短路,时间复杂度O(nm)O(nm)O(nm)。但是因为该算法的改进版SPFA,在求单源点最短路的问题上几乎总是优于Bellman-Ford算法,所以Bellman-Ford算法一般只应用在对边的数量有限制的最短路问题,即限制经过边的数量不超过kkk。Bellman-Ford算法属于动态规划算法。Bellman-Ford算法的基本思想是,如果要求最短路的长度最多为kkk(如果不限制,那其实就是k=n−1原创 2021-02-08 17:26:10 · 670 阅读 · 0 评论 -
【算法学习笔记】20:朴素Dijkstra与堆优化Dijkstra(无负权边单源点最短路)
Dijkstra算法用于在所有边权都非负的图上,求单源点最短路。设nnn是图上结点的数量,mmm是边的数量。则朴素Dijkstra算法的时间复杂度是O(n2)O(n^2)O(n2),适合稠密图(点少边多);堆优化版的Dijkstra算法的时间复杂度是O(mlogn)O(mlogn)O(mlogn),适合稀疏图(点多边少)。如果是稠密图,那么边的数量mmm和点数的平方n2n^2n2基本是一个规模的。如果用堆优化版的Dijkstra,那么复杂度就可以视为O(n2logn)O(n^2logn)O(n2logn原创 2021-02-08 00:17:08 · 2588 阅读 · 0 评论 -
【算法学习笔记】19:拓扑排序
1 简述计算拓扑序列的一个方式是,用BFS来尝试访问所有的节点,但是有一个约束就是只有入度为000的节点才能被加入到扩展队列里。每次从队列里取出一个节点,也就同时在图中将这个节点拆除,所以它的所有后继的节点都减少111,如果已经减少到000,那么就可以加入到队列中。在上面的例子中,一开始只有aaa的入度是000,所以先把aaa加入到队列中,队列中:aaa然后取出队头aaa并在图中拆除,然后它的后继ccc的入度变成111,bbb的入度变成000,把bbb加入到队列里,队列中:bbb然后取出队头b原创 2021-02-06 18:07:47 · 435 阅读 · 0 评论 -
【算法学习笔记】18:树与图的DFS与BFS
1 邻接表树和图的DFS和BFS,可以将树也看成图来存储,存储图的一个常用的存储结构就是邻接表。对于有向图而言,只存这个方向的边,对于无向图而言,存两个方向的边。在邻接表的实现中,用数组h来记录每个节点向外的边的链表头指针,初始时都是空(即-1)。用idx来表示链表节点的分配位置。数组e表示节点的值,即是目标节点的编号。数组ne表示节点的nextnextnext指针,也就是链表节点的下一节点被分配的下标。注意,当用邻接表去存无向图(或者树)的时候,因为a→ba \to ba→b和b→ab \to ab原创 2021-02-06 17:23:50 · 601 阅读 · 0 评论 -
【算法学习笔记】17:DFS与BFS
1 DFS深度优先搜索常用于解决需要给出所有方案的问题,因为它的搜索顺序就是能够得到一个完整的搜索路径(方案)后回退再去搜索其它的方案。1.1 例题:排列数字由于要求所有排列的方案,可以每次从1..n1..n1..n里拿一个数字,然后记录一下这个数拿过了,再递归地搜索下一个数字,当所有数字都取完之后,就得到了一种方案,将这种方案输出,回退之后去搜下一个方案。“回退之后去搜下一个方案”,其实就是在每层DFS的时候遍历一下所有的允许使用的数字,作为下一个位置的数字。需要注意的是在进入下一层DFS之前要把原创 2021-02-06 00:21:47 · 395 阅读 · 0 评论 -
【算法学习笔记】16:单调栈与单调队列
单调栈和单调队列都是一类算法思想,是栈和队列在算法里很常见的应用。单调栈算法一般用模拟栈或者程序语言给实现的栈都可以,因为只要求在栈顶入栈和退栈,以及取栈顶元素。单调队列算法很多时候用手写的模拟队列比较方便,因为很多时候需要双口出队的队列,主要是在队尾也有删除元素的需求。而模拟队列都是游标移动来限定队列中的所有元素,所以用模拟队列很自然的可以做到双端队列的操作。1 单调栈单调栈是栈中的元素按照一个单调性来排列,每次入栈一个元素xxx时,如果加入这个元素会使得单调性被破坏,就不停的弹出栈顶元素,直到可原创 2021-02-03 23:42:54 · 516 阅读 · 0 评论 -
【算法学习笔记】15:区间合并
1 简述区间合并问题也是一个贪心问题,由于比较常用所以单独拿出来。区间合并的解决方法是,把所有区间按照左端点lll从小到大排序,然后维护一个当前正在处理的区间[st,ed][st, ed][st,ed],如果遍历到区间和维护的区间有交集,就合并(能合则合),没有交集的时候,当前维护的区间就变成这个遍历到的区间。这里按照左端点排序好之后,每次遍历到的区间和当前区间[st,ed][st, ed][st,ed]只会有三种状态:情况(1)(1)(1)是l≤edl \leq edl≤ed并且r≤edr \le原创 2021-02-01 23:52:17 · 628 阅读 · 0 评论 -
【算法学习笔记】14:离散化操作
1 简述离散化是一种辅助解决问题的操作,当问题中涉及的数据范围非常大,但是实际使用到的数据是比较少的。并且问题的求解是和它范围里的其它数据有关系的,那么可以将这些可能使用到的数据放到一起,排序去重,就将它们映射到了一个新的较小的范围里。例如,下面几个数是在(−∞,∞)(-\infty, \infty)(−∞,∞)范围里的,实际用到的数:6、−10000、114514、1919、−123、19196、-10000、114514、1919、-123、19196、−10000、114514、1919、−原创 2021-02-01 21:43:50 · 779 阅读 · 0 评论 -
【算法学习笔记】13:双指针算法
双指针算法的核心思想就是在序列上找到这样一个性质,序列上的一个指针jjj随着另一个指针iii的移动,是单向移动的,不会发生回退,这样就能在移动指针iii的时候去移动指针jjj。由于两个指针都最多只会把序列遍历一遍,所以时间复杂度往往是从O(n2)O(n^2)O(n2)优化到O(n)O(n)O(n)的,也就是双指针算法能够优化掉时间复杂度上的一个维度。双指针算法中,两个指针可能是同向移动的(类似滑动窗口),也可能是对向移动的。1 例题:最长连续不重复子序列在这个问题里,试想用sis_isi表示以a[i原创 2021-01-31 22:59:33 · 243 阅读 · 0 评论 -
【算法学习笔记】12:前缀和与差分
1 一维前缀和前缀和可以用于快速计算一个序列的区间和,也有很多问题里不是直接用前缀和,但是借用了前缀和的思想。用s[i]s[i]s[i]表示a[0]+a[1]+...+a[i]a[0]+a[1]+...+a[i]a[0]+a[1]+...+a[i]的和(所以称为前缀和),那么要计算区间[l,r][l, r][l,r]的和,也就是计算a[l]+a[l+1]+...+a[r]a[l]+a[l +1]+...+a[r]a[l]+a[l+1]+...+a[r],只需要计算s[r]−s[l−1]s[r] - s[原创 2021-01-31 16:12:25 · 587 阅读 · 0 评论 -
【算法学习笔记】11:高精度整数A+B、A-B、A*b、A/b
高精度算法是在计算问题涉及的数据范围超过该程序语言的表示能力时,用数组模拟数学运算的一类算法。本节学习高精度的整数四则运算,其中乘法只要求一个因子是高精度,除法只要求被除数是高精度。以下,用大写字母(如AAA、BBB)表示高精度的整数,用小写字母(如bbb)表示用编程语言自带的整数类型就能表达的整数。以下默认是十进制数表示下的整数四则运算操作,在其它进制表示下也是类似的。1 高精度整数A+B模板题:高精度加法。要计算两个高精度整数AAA和BBB的和,可以用列竖式的思想,从最低位开始,将低位加起来然后原创 2021-01-30 17:47:56 · 2156 阅读 · 0 评论 -
【算法学习笔记】10:整数二分与浮点数二分
二分法用于解决这样一类问题:一个区间能分成左右两部分,左半部分满足性质AAA,右半部分不满足性质AAA。问题的目标是求解这两部分的分界点。所以二分法和区间里有没有单调性其实没什么关系,但是很多问题里是从单调性导出了上面的性质,上面的性质才是一个问题能用二分法求解的最本质的性质。二分法每次取区间的中间元素,通过判断区间中点元素a[mid]是否满足性质AAA就能断定求解目标是在mid的左边还是右边,从而每次把求解区间长度缩减一半。1 整数二分1.1 特点在整数二分问题里,需要考虑缩减区间时是否要保留m原创 2021-01-29 20:31:44 · 1030 阅读 · 3 评论 -
【算法学习笔记】9:归并排序与统计逆序对
归并排序归并排序的思想是基于分治法,其思路是:将待排序区间平分成左右两半,左右两侧分别递归地做归并排序。将这两个有序的区间合并(每次落一个较小的下来),就将这个区间排好序了。#include <iostream>using namespace std;const int N = 1e5 + 10;int a[N], tmp[N];void merge_sort(int q[], int l, int r) { // 如果区间里已经<=1个数了,就直接返回原创 2021-01-28 22:46:22 · 667 阅读 · 0 评论 -
【算法学习笔记】8:快速排序中的边界问题
基本思路快排的思想是基于分治法,其思路是:确定分界点:给定要排序的数组q,先在数组中找一个数作为分界点值x,分界点可以取左边界值x = q[l],可以取右边界值x = q[r],可以取数组中间的数x = q[l + r >> 1],也可以随机取一个。调整区间:将数组划分成两个区间,使得左半部分所有的数都<= x,右半部分所有的数都>= x。递归处理左右两个区间。快排有多种实现方法,在y总的模板里,分界点的位置不一定是x,因为x参与交换之后仍然会被留在左右区间中的一个里。原创 2020-11-20 13:54:51 · 5830 阅读 · 22 评论 -
【算法学习笔记】7:字符串前缀哈希法
字符串前缀哈希法有很多字符串的问题可以用字符串哈希来做,不一定要用KMP算法。这里的哈希方式是一些比较特殊的哈希方式,即字符串前缀哈希。比如有一个字符串abcde,则可以先预处理出来所有前缀的哈希,比如h[1]就表示a的哈希,h[2]就表示ab的哈希,特别的,定义h[0]=0表示前0个字符的哈希值为0。要定义某一个前缀的哈希值,只要把字符串看成是一个PPP进制的数,那么每一位的ASCII码就表示这一位的数字是多少。那么上面的例子的字符串哈希值(即对应的十进制的数值)为:a×P4+b×P3+c×P2原创 2020-11-07 22:27:56 · 2197 阅读 · 4 评论 -
【算法学习笔记】6:SAT问题的一些经典求解策略
1 问题描述布尔可满足性问题是给定一个合取范式(CNF),即一系列析取形式的子句(clause)的合取式,问是否存在一组赋值使得整个式子为真。给出满足条件的赋值(说明是satisfied的),或者证明不存在这样的赋值(说明是unsatisfied的)。显然如果要整个CNF为真,则需要每个子句都为真。而对于每个子句内部是析取,所以需要至少有一项为真子句才能为真。2 不完备算法一类SAT求解算法...原创 2020-05-08 12:40:30 · 8010 阅读 · 0 评论 -
【算法学习笔记】5:基于蚁群算法的柔性作业车间调度问题(FJSP)快速求解
简述这是《深度学习与人工智能》课程中很普通的一道作业题,但因为发现了一个更巧妙的搜索目标的形式,让求解过程快了很多,代码实现起来也简单了非常多,而且最终的搜索效果也更好。关于蚁群算法和柔性作业车间调度问题不再赘述。求解策略比较如果用这篇文章中的方法,求解这个问题会很困难。因为同Job的不同工序是有先后顺序的,如果直接在上面这张表里搜索解,也就是说搜索出的是这张表里每一行标一个机器,那么接...原创 2020-04-26 01:22:22 · 8466 阅读 · 12 评论 -
【算法学习笔记】4:贪心法,回溯法,分支限界法,解空间树剪枝
[4]贪心算法贪心算法不从整体最优上考虑,而是在局部最优上做出选择。对于很多问题贪心法不能得到整体最优解,但对于某些特殊的问题,仍然可以得到整体最优解。使用贪心算法应满足这些性质: ①最优子结构性质:一个问题的最优解包含的子问题也是最优的。 ②贪心选择性质:整体最优解可以通过一系列局部最优的选择,即贪心选择来达到。活动安排问题n个活动的集合E,都需要使用同一资源,活动i有开始时间si和原创 2019-08-17 22:38:09 · 9169 阅读 · 0 评论 -
【算法学习笔记】3:动态规划
[3]动态规划动态规划思想和分治类似,也是将问题分解成多个子问题,通过求解子问题来获取原问题的解。 用动态规划求解问题应满足的性质:①最优子结构性质、②重叠子问题性质。 ①重叠子问题性质:每次产生的子问题并不总是新问题,有些子问题会重复出现多次,可以把它们保存下来。 ②最优子结构性质:如果一个解是最优解,那么这个解所包含的子问题也一定是最优的。动态规划一般可以用这两种方式实现: ①原创 2017-11-27 11:28:09 · 1035 阅读 · 0 评论 -
【算法学习笔记】2:渐进分析记号,复杂性比较,递归,分治
[1]算法概述算法与程序 算法:由若干条指定组成的有穷序列,具有输入(零个至多个)、输出(至少一个)、确定性(无二义性语句)、有限性(执行次数和时间有限)、可行性(每一步都可实现)。 程序:算法用某种程序设计语言的具体实现,可以不满足有限性(如操作系统)。算法复杂性 时间复杂性 T(n):需要时间资源的量。 空间复杂性 S(n):需要空间资源的量。算法复杂性集中反映算法的效原创 2017-11-26 17:47:51 · 2303 阅读 · 0 评论 -
【算法学习笔记】1:回溯法中子集树与排列树(装载/最大团/n皇后/旅行商)
解空间就是所有解的可能取值构成的空间, 一个解往往包含了得到这个解的每一步,往往就是对应解空间树中一条从根节点到叶节点的路径。子集树和排列树都是一种解空间,它们不是真实存在的数据结构,也就是说并不是真的有这样一棵树,只是抽象出的解空间树。约束条件Constraint是问题中就限定好的条件,比如在装载问题中装入第i个物体后不能超过背包总容量时才考虑装入它,即搜索左子树的情况。限界条件Bound是需要自己挖掘的一个原创 2017-11-05 21:22:08 · 7136 阅读 · 5 评论