Algorithm Note —— 算法之上的算法

最近一下子学了很多新知识,于是把这一段时间的所思所想先记录下来,方便以后回头再看,补充细节分分类

8.27

优化主要还是从循环下手,明确每一层循环和每一个函数的实际作用,不能只停留在形式上而是要看实质,看看有没有进行多余的判断或运算,要紧紧抓住和问题的本质相关的条件,而和问题本质无关的条件可以用“消元法”消去,我们不能被花哨的题目描述遮蔽了双眼,在保证相同功能的条件能不能利用一些性质(比如组合性,拆分性,单调性等)来改变写法或者用一些数据结构(堆/前缀和/单调栈/单调队列/树状数组/线段树等)来避免这些多余的运算(加速dp转移)

树状数组可以求前缀和或者前缀最大/小值,常用于两维单调动态规划(比如逆序对,LIS这类问题的两维单调特征就很明显),两维之间可以相互调换(也叫作离散化)

线段树在区间问题上用的更多一些,比较灵活,常用lazy标记

再者是数学公式的优化,经常要拆开来看有没有重复计算的地方,或者用其他公式来转化(比如说莫比乌斯,费马,欧拉等),再用一些算法比如说线性筛什么的,基本优化优化就能过了

一些比较简单的关系可以直接用矩阵表达出来,再用类似于快速幂的方法二分求出答案,把O(n)变为O(logn),例如求斐波那契数列

有些题的数学形式可能不是很明显,但是往往会涉及到一些比率相关的问题(比如说分数规划),然后用二分等等……需要做进一步的转化

再还有一些比较常用的比如二进制拆分,素数因子分解等等

求最大值最小等问题、在单调区间中找某个值、或者可行与不可行间有明确的取值分界(划重点,陷入困境的时候常用)并将答案取值分为两个部分的时候可以考虑用二分

当二分求mid比较麻烦的时候(比如说在树和链表里)可以预先递推计算距离2^n的点的位置,然后把二分转化成倍增(如LCA的树上倍增的做法),其实它们是一家的,不过倍增的分法不是严格二分,而是有点类似于……树状数组?要是这样比的话二分和线段树也很像

当然,在链表上通常用skiplist,不知道有没有类似的可以动态维护的skiptree(要不然在树上动态维护LCA还挺难),也许树链剖分能做?……上网看了别人的做法,貌似两种都可以?当然,对LCT一无所知的我还是选择用跳表来做

树状数组好像可以和树结合起来(这不就是树链剖分吗……——9.27),可以用来求祖先中的前缀和最大/小值等等,但是lowbit对应的位置要提前求,省里培训的时候有两道问最近标记点的问题(在树上和数组上)好像可以用这个来做,不过当时讲的是用并查集来做,还有待考察

动态树/数组问题可以用跳表,跳树?,LCT(反正我们现在还没有遇到过这类问题)

静态的话直接lowbit,倍增就行

还有一些有趣的东西,比如邻接矩阵的k次幂,网络流,解析几何,莫队等等

printf输出浮点数会自动四舍五入

有些题可以贪心,或者是结合替罪羊来贪心(其实贪心和dp一样,都是状态的转移)

需要证明某些东西的性质时往往要和它的定义相联系,在定义下往往会衍生出一些有用的性质(比较典型的就数单调性了)

有空再总结一下DP的常用技巧

8.28

今天做第一题的时候偷了个懒,用递归来离线解在线问题,结果栈爆了,解了半个小时的通项公式就这样付之东流……想起来以前似乎也没少在递归上面栽跟头,谁让学校用Windows测评呢,今天定量研究了一下,发现递归到1W+层的时候会崩溃,所以以后还是要在这里多注意一下

差分是一个比较常用的统计方法,先单点更新,再在整个数组或树上更新,比较适合不需要在线处理的问题

当然就算是在线处理的问题也可以不完全差分,只在需要查询的地方更新,比如说在线段树上打的lazy标记(当然,没有人说lazy标记不能打在一般的树上)

8.29

如果把数据都抽象成点,再以此为基础向上搭建………………

………………构建数据结构的思路会不会不一样?

8.30

今天把之前研究了比较久的单调性问题写出来,因为很多优化都是从单调性(或最大/最小值)入手的

但是优化毕竟就是优化,如果遇到一道题什么都不分析就直接把xx优化方法硬往上套很容易gg

回顾之前做出来的题,发现他们的优化都和单调性的应用离不开,毕竟数据之间真的一点可以利用的性质都没有的话数据结构就没有用武之地了,当然就算不单调你也可以用模拟退火等等搞

没了order就意味着数据是杂乱无序的,只能用$O(n^?)$乱搞

单调的维度通常都是时间前后,空间位置,编号顺序,数值大小(的导数?),还有的是答案本身等,有一些的单调维度是显而易见的(比如说时间或编号),而经常同时存在的另一个单调的维度就需要自己去发掘了(有可能是一个比较复杂的关系,需要自己分析)

当然有些单调性可能原本在数据中是体现不出来的,需要自己来维护这个单调性(离线用sort,在线用heap)

有些人喜欢先入为主的去区分key和value,但我认为这只是问题中的两个维度,而这两个维度常常是可以交换的,比如说key的范围远大于value的情况,如果像之前那么做的话需要先把key进行离散化,但是换个角度把key和value交换一下就可以巧妙地避免离散化

在另一维上求最大/最小值,这时候用单调栈/单调队列

在另一维上求前缀和的时候用树状数组

如果另一维的导数(斜率)是单调的,意味着这是个(凸包?),如果仅仅是求最大/最小值可以用三分法求极值,如果这是在DP里的话就意味着你可以用斜率优化了

二维树状数组到底是个什么东西……

…………

不排除另一维比较鬼畜的情况,这时候就要用最优化方法来处理了:http://www.cnblogs.com/maybe2030/p/4751804.html

==》另外,我认为最大/最小值仅仅是单调性的一个简化形式

过几天再补充一些图论的东西

8.31

 简单讲一讲DP的各种姿势(划掉)

果然之前没完全弄明白到最后还是搞不出来

所以说树上的order能不能看成是单调的维(似乎可以(那么就可以套用之前的知识了(顺便还能各种优化(但还是感觉不太懂的样子(大雾?)))))

9.1

开学了

正在学习ac自动机(机是算法的意思),后缀数组,和网络流

9.2

时间复杂度高的算法是否一定可以优化到时间复杂度低的算法(貌似不是?不过很多时候可行)

MST?

简谈树形数据结构,各个分支必须保证明确可辨(需要用到数据中的性质,比如说大小关系等等)(有点类似于二分啊……)

同样是支持快速动态维护的数据结构,treap和skiplist孰优孰劣?

9.3

谈谈DP(动态规划)

1、最优子结构  2、无后效性  3、子问题的重叠性

 由于编程求解递归关系的时候会伴有大量冗余操作,而且是指数级增长的,而这往往是优化的对象

注意到我们只关心状态和状态之间的转移关系,而转移关系不一定要通过递归来实现,我们只需要保证转移所需的状态在这之前已经计算完成即可,这就是DP

DP的核心在于状态之间的转移关系(即状态转移方程)

设计DP的转移是有技巧的,往往对应一些最小最基本的动作(准确讲是状态变化的最小单元)

如果转移关系是简单的线性关系,那么就可以矩阵快速幂来做

线性关系通常表现为每一步都在做相同的事情,而且方程里没有min/max

为了保持最优子结构,设计DP的时候需要完整描述出转移的时候所需要的状态,不能有歧义,通常是通过下标来描述状态(有时需要状态压缩,但这不是问题的本质)

怎么定义状态也是需要仔细考虑的,这个需要具体问题具体分析

如果描述状态的方法太复杂的话可以用hash

常见DP:http://blog.csdn.net/csyzcyj/article/details/9324057

9.5

晚自习也上竞赛课,感觉生物钟要爆炸

9.13

题目里需要用到PI,我直接带3.1415926来算,结果因为精度太低丢了30分,经过查找才知道原来cmath里已经定义了常量M_PI,而且精度很高,以后就用它了

9.14

DP居然也可以同时转移多个变量(当然,前提是这几个变量描述的必须是同一个最优化状态)

比如今天的题同时维护一个状态所需的最少代价和结尾最小值,事实上两者一定是指向同一状态的(同一状态的两个值)

其实这道题也需要用到决策的单调性(重点),前面的塔向后合并可以看成后面的塔向前合并,所以说对于每个塔我们只需要考虑它连续向前合并了几个塔然后动态转移即可(如果不这么考虑就会陷入决策的混乱(即后面的能向前合并,前面的又会向后合并,非常ZZ的想法),导致推不出来转移方程。

9.15

上面总结的DP技巧真是屡试不爽,推了20分钟之后非常稳的A掉了今天的第一题,开心

(看了下出题人的原题包,除了std竟然没有人AC这道题……)

为什么用树形结构往往比链式结构快呢?因为树形结构最大化地利用了数据之间的关系(其实二分法也是一样)

9.16

我还以为链式前向星是什么很高端的东西,原来就是平时一直写的邻接表……

9.23

补集思想……interesting

各位dalao简直太神了……替罪羊树,区间第k小元素,树分治,点分治

9.28

$3^n$枚举子集的方法

http://blog.csdn.net/hcbbt/article/details/35328353

1 for (int s=0;s<(1<<n);s++)
2     for (int i=s;i>0;i=(i-1)&s)

 原理:s&(i-1) 实际上是把s中的0全部忽略,并不断减1的结果。

 

树上各种高端操作

http://blog.sina.com.cn/s/blog_97a3fe860101e8oc.html

同时有修改子树、修改路径、查询子树、查询路径的题目:
先树链剖分,然后dfs序,dfs时候先dfs重链的那个儿子,这样每条重链在dfs序上也是连续一段。
这样子树在dfs序上还是一个区间,而路径可以拆成log条重链,在dfs序上对应log个区间
这样就同时支持上述四个操作了。

 

简谈模运算与群论

http://www.matrix67.com/blog/archives/6504#more-6504

小点的每一步运动都形成了一个置换,三个置换的复合本质上也还是一个置换,而这个置换的足够多次幂一定会变成单位置换。这意味着,不但每个点都能回到自己原来的位置,而且所有点能同时回到自己原来的位置(后者可能需要更长的时间)。事实上,有限群中的任意一个元素都有一个有限的阶,因而如果某类变换操作能构成一个有限群的话,不断地执行某一个操作,或者不断地循环执行某几个操作,最后总有一个时刻你会发现,一切又都重新变回了原样。拿出一副新的扑克牌,每次洗牌时都把牌分成两半并把它们完美地交叉在一起,那么不断这样洗下去之后,整副牌总会在某个时候重新变得有序。找一个复原好了的魔方,循环执行几个固定的操作,魔方很快就会被彻底打乱,但最终一定会奇迹般地再次复原。

那么斐波那契数取余、欧拉定理和费马小定理(幂取余)不也是如此吗,实质是多个运算法则产生的置换群,而只要置换的足够多次幂一定会变成单位置换,也就是重复开始它原来的循环

而欧拉定理和费马小定理往往是解决这类问题的利器

详见Fermat-Euler定理的群论证明及其变形:http://www.cnblogs.com/algonote/p/7606218.html

原谅我这么久以来一直以为数论毫无规律可循,仅仅是数学家无聊编织的迷宫,简直都要对数论无爱了,现在才发现原来它其实也是一个体现统一对称美的学科

9.30

上个月还觉得自己的DP很辣鸡的说

不过随着个人的总结,最近发现自己做DP题真的是越来越简单了

10.2

DP题有的时候本身没有明显的单调性,但如果一些元素之间的顺序关系对结果没有影响的话我们不妨自己规定一个顺序,以此来避免不必要的计算过程(在状态描述或转移方程中记录不必要的信息会增加DP的维度),降低时间复杂度(愤怒的小鸟,不等序列,打砖块)

10.8

求$n!$中含p因子的数量

$\sum^{+\infty}_{i=1}{\frac{n}{p^i}}$

http://www.cnblogs.com/huihao/p/7636068.html

 

《论当今信息学竞赛中数学建模的灵活性》——

我们在拿到题目时,不能急于动手编程,而首先应该冷静地去思考分析问题,并从与之关联的各种信息中,正确地“过滤”掉迷惑人的无用的部分,“提炼”出关键的部分,从而很好地把握这“新面孔”背后隐藏的“老模型”。俗话说,磨刀不误砍柴功。只有经过了周密深入的思考,我们才有可能透过现象洞察到问题的本质。而此时再着手编程,就能胸有成竹,事半功倍。

“虚”的多维化

(1) 定义
  在上述“实”的多维化中,“维”沿用了它的本义,即构成“构成空间的因素”。而在“虚”的多维化中,“维”的含义可以引申为广义的“构成数学模型的因素”。
由此,“虚”的多维化的含义就是“构成数学模型的因素”的增加。  

(2) 表现和策略
  区别于“实”的多维化,“虚”的多维化的是以增加阶段参数或状态参数等形式体现在我们的数学模型和程序当中的。这既是“虚”的多维化的表现形式,也是相应的解题策略。
  其中最为典型的就是动态规划模型的多维化。(图论中的标号法可以看成是动态规划的优化,因此,这里把标号法也归入动态规划。)
    我们知道,经典的动态规划是由阶段、状态、决策三重循环构成的。但是,如今的动态规划,常常不再局限于这陈旧的三重循环模式,而是借助于阶段、状态变量的增加,将模型建筑到了多重循环之上。

https://wenku.baidu.com/view/89bffa00bed5b9f3f90f1ca0.html

10.10

看似是数学题不一定是数学题,看似是字符串题不一定是字符串题

做题的时候一定要灵活思考,做题的时候先要发散思维,尝试从多角度分析,避免直接落入思维陷阱,不要认为自己某些类型题做的多了就可以按定式硬往上套

10.15

NOIP初赛Day2网赛,3道思博题,数学,暴力数位DP,贪心,写完之后不知道该干什么,于是写了两个bat对拍了2个小时……

10.21

新学了KDtree这个东西,虽然很久之前看过相关的文章,知道了原理,但是一直没写过,今天照着hzwer的模板写了一遍,感觉实现过程很有意思

感觉这个KDtree可以DP放在一起搞

莫队算法真是巧妙通过对询问的排序就能把复杂度降到$O(n\sqrt{n})$

10.22

在树上离任意一个点最远的点一定是树的直径的两端点的其中之一,可以用我发明的“撸树法”证明

求方案数的题经常让人凌乱,想出的统计方法要不然就是统计少了,要不然就是统计多了,为了防止重复统计方案数有一个重要的技巧:

如果方案内存在多个元素的话不妨统计选取其中第一个元素(或最后一个)的方案数,这样就能很好的保证统计时不重不漏

这个结论也可以推广到更广泛的形式上:

对于同时包含多个元素的问题(多元组),很多时候只要抓住开头或者结尾元素就行了

用到这个技巧的例题:逆序对(每个逆序对中靠右边的元素),Nescafe25信号塔(连续合并的一段塔中的最后一个塔),Nescafe28塔顶试探(树根再次出现的第一个位置),Streaming5高维网络(路过的第一个被破坏的点),Day10.11序列问题(左集合的最后一个元素),Day9.5集合(队列中的最后一个元素)

DFS序妙用:在树上判断一个点是否在两个询问点间的路径上可以用DFS序,如果它在两个询问点的LCA的子树中,且询问点中有某个点在它的子树中,那么它就在这一条路径上。

有空把上文中和DP相关的部分单独拿出来整理成一篇文章

10.28

看似暴力的做法复杂度却不一定很高

比如平均化问题,通常O(logn)就会达到稳定

一开始时间复杂度很高,后来时间复杂度很低,时间复杂度有可能是O(sqrt(n))的

 

再谈DP(动态规划)

设计动态规划方程可以从转移动作本身来构建,当然也可以通过对搜索的转换来构建

搜索的和状压DP的时间复杂度是相同的,因为他们在每个状态中都记录了之前所有的的信息,两者的本质是一样的

有的时候用搜索可能更优,可以避免处理不必要的状态

而我们往往会发现在这个过程中记录了一些不必要的信息(即与状态转移无关的信息),这时我们可以选择在状态中不记录这些信息从而来降低时间复杂度

有时我们又会发现需要记录一些有后效性的信息以保证状态本身是无后效性的,这时的时间复杂度往往是增加的

10.29

有些求方案数的题需要会用到异或的思想,答案往往是$2^基$,不过需要先证明他们之间是一一对应的,最好先写个暴力程序验证一下

用到这个技巧的例题:Nescafe17黑魔法师之门(元环不同的异或方法和“每个点的度数大于零且都是偶数的子图”是一一对应的),Day10.29植树方案(“根到其路径上的边权异或”和不同的植树方案是一一对应的)

 

谈谈DP的技巧——如何降低状态的复杂度

用从原点出发的环MOD压缩路径(长途旅行),强制对齐使得只有前k行的状态是不确定的(骨牌覆盖)

11.1

 dfs序七个经典问题

http://www.cnblogs.com/weeping/p/6847112.html

这篇文章写的非常棒,详细介绍了在线/离线+树链/子树/单点修改+树链/子树/单点询问的解法

 

退役后杂文

转载于:https://www.cnblogs.com/algonote/p/7441725.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值