基础算法与数学总结 【第一部分 基础算法】

基础算法与数学总结 【第一部分 基础算法】

本章并非没有很多可讲的,但是限于篇幅,有些本来能提三级标题的内容只能放进列表了列举了。

1 位运算

位运算是计算机进行计算的底层原理,位运算是指计算机对于数据转换为二进制后进行的操作。

这一部分主要是一些技巧,包括:

  • 位运算基本性质

    • (C)2,C=1(C)10\forall (C)_2,\sim C = -1-(C)_{10}

    • n1=n2.0n \gg 1 = \lfloor \frac{n}{2.0}\rfloor

    • 在二进制表示下不进位

  • 自然溢出

    对于unsigned变量,例如ull,即自动对 2642^{64} 取模。注意signed变量算术上溢是未定义行为。

  • 状态压缩

    使用二进制数表示boolean状态的方式。拥有表示方便易于枚举与存储的特点。

    至于如何操作二进制位就太过基础。请在操作时注意运算优先级。

  • 成对变换

    对于所有非负整数n,如果n为偶数则满足 n1=n+1n \oplus 1 = n+1

    为奇数则相反。用于Tarjan求割边或网络流

  • lowbit运算

    lowbit(n)=n&(n+1)=n&(n)\operatorname {lowbit}(n)=n \And (\sim n + 1)= n\And (-n)

        它的实际意义是求 nn 的二进制表示下第一个(从右向左)1以及前面的0一起构成的二进         制数,常用来遍历这个数的二进制表示下的所有1。用于树状数组。

2 递推与递归

递推与递归的思想底层都是要把问题规模化小(即划分思想),然后从边界的状态(已知答案的小问题)推得所求问题的答案。

在这一方面,递推是有顺序、有目的地从初始状态开始正向推。而递归是从上往下进行搜索尝试缩小问题规模以触达边界,然后顺着路线回溯。

可以使用递推与递归解决的问题,要求对于每一个状态要有相似的处理过程,以及明确的边界(至少应该存在)。

所以,如果问题具有少而明确的边界,可以考虑使用递推,否则考虑使用递归。

3 前缀和与差分

前缀和S的定义式:

S[i]=j=1iA[j]S[i]=\sum_{j=1}^{i}A[j]

容易发现,如果对于可以差分的信息,还满足:

sum(i,j)=k=ijA[k]=s[j]s[i1]sum(i,j)=\sum_{k=i}^{j}A[k]=s[j]-s[i-1]

同样这种方法可以扩展到二维、树形、复杂信息等领域,这些也许需要进阶数据结构来优化处理。

差分C的定义式:

C[1]=A[1].i[2,n],C[i]=A[i]A[i1]C[1]=A[1].\forall i \in [2,n],C[i]=A[i]-A[i-1]

差分与前缀和是互逆的运算,常用于将区间修改转化为单点修改

4 二分

二分是分治的一种应用,分为二分查找与二分答案,还有实数域上二分、三分法等技巧。

二分基本思想就是通过备选集合单调性来减小问题规模。

所以它的应用最基本的条件就是答案要有单调性,详细来说:

如果存在和若干条件 ciCc_i \in C ,现要求一个最大的x满足C中所有条件,那么设最优解为 SS 如果这个问题满足在S的一边不合法,另一边合法,那么这就是满足单调性的问题。在合法的一半,你知道最优解可能更大,如果不合法,就知道解肯定更小。

发现二分单调性需要眼光。

If it is difficult, why not the opposite?

实数域上二分与三分法求单峰函数极值就不说了。

5 排序

也就离散化用的常一点,一般来说,排序算法的具体实现都不需要知道。

对于一些问题例如逆序对问题、kth可以使用归并排序(即一种CDQ分治应用)解决。其他问题中一般都是一个工具。

6 倍增

当状态空间很大并且在正整数域内连续(对于2的整次幂具有可划分性),那么可以”只选2的整次幂“作为代表点。然后要用到其他的值时,就利用2的整次幂的性质进行合并。它是”倍增“与”二进制划分“的合并。其在很多数据结构和算法中均有应用,可以被分为”预处理“和”拼凑“两个阶段。

例如ST算法是解决RMQ问题的一种,可以看作其实是区间DP的倍增优化。

7 贪心

贪心是十分重要的思想,它的使用可以帮助避免无效的状态空间遍历、提升时空效率、降低编程复杂度,甚至有时还能作为骗分利器。

贪心是一种每次在决策的时候都采用当前最优策略的方法。使用贪心,要求整体上的最优是可以由局部最优推导而来的,就例如求解SSSP问题的Dijkstra算法,就使用了贪心的思想,但是这种性质只有在代价非负时才成立。

当然,还有一种奇妙的贪心叫反悔贪心,它首先选择当前的局部最优解,然后又采取某种策略进行调整。

由于贪心可能不是正确的,所有要使用的贪心算法都需要证明,常见的证法:

  • 微扰

    证明对局部最优解做的任何变换都会使得答案变差

    使用这种策略,常常将决策序列中的某两项进行邻项交换。

  • 范围缩放

    证明对于任何规模的问题应用贪心策略都不会使得结果变差

  • 决策包容性

    证明做出局部最优解决策之后所能做的决策包括所有做出其他决策后的可能性

  • 其他:反证法,数学归纳法

要考虑贪心算法,常常都要从小范围、小规模的问题出发,或是一一模拟决策过程,找出局部最优决策后进行推广+证明。这告诉我们,解决问题最好动手模拟,而不是盯着题面望眼欲穿。


贪心,常常与玄学挂钩,因为很多时候的贪心都未经证明或是很难看出,这里笔者就没有别的办法了。至于上面的方法有用没用就难说了。

8 例题

位运算的特征就是“不进位”,这决定我们可以分开一位一位处理。

分形正符合递归“调用自己”的特点,同时它的边界状态也很明确,只是变化之后变复杂了不方便直接数学方法推导。

我们选择规模1级的城市作为边界状态,然后进行递归。

差分区间修改转单点修改,然后计算前缀和。

ST表是倍增的经典应用。本质上是对区间DP的优化。

从这里面我们也可以再一次窥见倍增思想的本质就是数据2进制分块+拼凑

贪心问题通常是逐步进行的问题,通过以下步骤,能够使这类问题的思考更加具有条理性

  • 从小范围、小规模的问题出发

  • 模拟决策过程,找出局部最优决策或者一些性质

  • 试推广最优决策,或者用性质转换问题

  • 证明

本题中,通过树中除根以外权值最大的节点一定会在其父亲被染色之后立即被染色,将其父节点与它自己合并。然后再试确定这样的合并节点与其他节点的染色顺序,通过代数变换得到等效权值。

首先应该考虑,情况下可以喝到可乐?

有人说,题目不是明明写着异或吗。

没错,但是异或是位运算,要使用位运算的基本性质或者是位运算的一般思路(当然数据范围也是重要参考标准),那么必须把条件转化到位运算尺度才行。位运算,当然是要逐位考虑。

从高到低位考虑,如果k这一位为0,那么x这一位异或 aia_i 这一位必须等于0,否则就肯定超出k。反之,则没有限制。

然后我们发现,每种情况带来x的取值都是一段区间,所以可以转差分维护。

posted @ 2023-07-21 08:08  haozexu  阅读(28)  评论(0)    收藏  举报  来源