暴力求解法

迭代加深搜

适用于搜索树深度不确定的时候,可以使用迭代加深搜。

步骤:

1.枚举maxd表示最深枚举深度;
2.假设当前深度为g(n),乐观估计至少要h(n)层才能到达叶子节点,那么g(n)+h(n)>maxd时,就应该剪枝。

在我理解看来,乐观估计的意思是说不去管所有的限制,然后去计算当前点到终点的距离或者所需要的操作数量,即还需要h(n)层,这时候的h(n)才是最好的,即最小(大)的,可以利用来剪枝。A*就是把状态乐观估计还要h(n)层才到达的想法使用到bfs上面。

双向搜索

对普通搜索的改进,使用的是BFS,大体流程就是从起点开始向终点搜,终点开始向起点搜,然后就直到第一个搜索碰到第二个或者是第二个碰到了第一个,然后答案就是两个搜索步数的和再减去1。比较典型的例题有魔板,然而那个题目一共3个变换方式,还可以不用双向搜索。

分治算法

数列上的分治

顾名思义,数列上的分治就是日常用的分治,最经典的例题就是求逆序对数量。使用的算法是归并排序,比较两个指针所对应的数字,如果左边的大于右边的,那么就说明,当前左边的指针到mid的所有数字都是大于右边指针所指的数字,那么答案就应该加上mid-l+1。

CDQ分治

可以代替数据结构的利器,可以解决的问题是区间加和单点修改。
\(\color{red}{流程:}\)
1. 按所有的按所有的操作等分分成前后两部分
2. 处理前面部分的、后面部分各自的修改和查询
3. 处理前面部分修改对后面部分查询的贡献(动态查询变成静态查询)
听老师说很好打,而且用处也很大,这个CDQ分治处理的问题需要满足离线操作和修改与修改之间互不影响。因为是把动态询问变成了静态询问,所以还可以通过分治把某些排序可以做好,更好地进行静态查询。

树分治

树的重心是指以该点为根时,最大子树节点数最小。子树节点数都<=n/2,层数也是有logn层,每一层都可以在O(n)的时间内处理问题。

贪心算法题目选讲

贪心,我的理解就是就当前状态来看,所选取的最优决策,但是不一定会是全局最优决策,所以很多题目看似是贪心,但是贪心并不正确,只能看直觉,毕竟证明很麻烦。典型例题是铺设道路(积木大赛),就是在当前点时,对比上一个点,如果比上一点大,那么加上多出的一部分,反之,就是continue,然后更新last为当前点的大小,last初值为0.

简单数学

欧几里得算法

可以求出变量a,b的最大公约数。

\(gcd\)代码:

int gcd(int a, int b){
return b == 0? a : gcd(b, a % b);
}

扩展欧几里得算法

可以求解类似于\(ax+by=c\)的不定方程,有整数解的条件是\(gcd(a,b)|c\),即是在说\(gcd(a,b)\)必须是\(c\)的因子。

\(exgcd\)代码:

void gcd(int a, int b, int &g, int &x, int &y){
if (!b) {g = a; x = 1; y = 0;}
else{gcd(b, a % b, d, y, x); y -= x * (a / b);}
}

式子推导如下:
\(ax+by=gcd(a,b)\)
\(b \times x+(a- a/b \times b) \times y=gcd\)
\(y \times a+(x-y\times a/b)\times b = gcd\)

线性筛素数

学习过三种筛法,第一种,也是最耗费时间的就是,从2开始向n-1枚举,对其稍加优化就是枚举到 ,但是根本不会满足需求。于是就有了埃式筛,时间复杂度可以做到O(nlognlogn),可以说是很接近线性了,但是又不是真正的线性筛,所以还需要优化。欧拉筛成功的做到了线性筛的要求,在O(n)的时间内筛出素数。

埃式筛代码:

for(int i=2;i<=t;i++)
if(prime[i])
for(int j=2 * i;j<MAXN , j += i)prime[j]=false;

欧拉筛代码:

for(int i=2; i<=n; i++){if(!vis[i]) prime[cnt++]=i; for(int j = 0; j < cnt && i * prime[j] <= n; j++){ vis[i * prime[j]]=prime[j]; if(i % prime[j] == 0) break;}
}

逆元

我理解的逆元就是一个数在模p的意义下的倒数。现在也有了多种求解逆元的方法,比如费马小定理,递推式求逆元,扩展欧几里得求逆元,当然用的最多的莫过于递推式,因为很快的就能求出。
组合数
有一个公式 ,然后是就是围绕着它进行推导,比如 等等。

数据结构

堆有两种,一个是大根堆,另一个是小根堆;一个是less,另一个是greater。
头文件是

#include <queue>
priority_queue<int>q;

支持的操作有:
插入,时间复杂度为O(logn);
查询,时间复杂度为O(1);
删除,时间复杂度为O(logn).
如果卡常数的话可以使用make_heap之类的。

二叉搜索树

树高期望 \(logn\)
操作:插入,删除,查询
例如STL中的set,map

线段树

线段树可以维护区间信息的数据结构,每一个节点都对应着一个序列的区间。
可以支持单点修改或者是查询。

动态开点

合并 复杂度就是两颗线段树重合线段树重合节点个数

tree *merge(int l, int r, tree *A, tree *B){
if(A == NULL) return B;
if(B == NULL) return A;
if(l == r) return new tree(NULL, NULL, A -> data + B -> data);
int mid = (l + r) >> 1;
return new tree(merge(l, mid, A -> ls, B -> ls),merge(mid + 1, r, A -> rs, B -> rs), A -> data + B -> data);
}

树状数组

一个十分好用的数据结构,其重点就是lowbit函数。只能维护满足可减性的量,例如和,异或和,而与,或不能用树状数组维护。线段树包括了树状数组,但是树状数组常数小。

二维树状数组

与一维树状数组类似,只是多了一层循环。令s[x][y]表示x-lowbit(x)+1<=i<=x, y-lowbit(y)+1<=j<=y的a[i][j]的和

并查集

按秩合并+路径压缩可以证明复杂度是对的,但是也可以只写路径压缩,是卡不掉的。

例题就是洛谷P1525关押罪犯。第一个做法是二分+二分图匹配,第二个做法是并查集。

Trie

代码:

//trie树,字母集
int rt=1,cnt=1;
int ch[N][26];
void insert(strint s) {int o=rt;
for(re int i = 0 ; i <s.size;++i) {
int k=s[i]-’a’;
if(ch[o][k]) o=ch[o][k];
else ++cnt,o=ch[o][k]=cnt;
}
sz[o]++;
}
int query(string s) {
int o=rt;
for(re int i = 0 ; i <s.size ; ++ i ) {
int k=s[i]-’a’;
if(ch[o][k]) o=ch[o][k];
else return 0;
}
return size[o];
}

Trie树还是蛮重要的,数也可以上trie树,但是需要变成二进制,这样的话在一个序列中寻求两个数的异或最大值就变得很容易求出来。比如说是异或最大值,我们让每一个数字都拆分为二进制,然后上trie树,总是选取1,如果没有1再去选取0,这样最后出来的数字就是最大的异或值。原理就是1000,总是会比0100好,更加优,所以一直选取1,没有1再取0.

Hash

19260817是一个常用的mod数,素数。

Hash碰撞

本来不相同的两个数可以在某一个数的时候会相同,被称为hash碰撞。为了避免可以使用map映射或者双模数。还有一个方法就是自然溢出,自然溢出加上双hash还会更好,不容易被卡。

RMQ问题

对于长度为n的数列A,回答若干询问RMQ(A,i,j)(i,j<=n),返回数列A中下标在[i,j]里的最小(大)值,也就是说,RMQ问题是指求区间最值的问题。
St表
令f[i][j]表示从i开始,2^j个数的最小值
f[i][j]总共有nlogn个状态
可以由j从小到大递推求出
静态查询最好使用ST表,这样的复杂度是最优的。
递推式为

f[i][j]=min(f[i][j-1],f[i+(1<<(j-1))][j-1]);

求LCA

1.倍增求LCA

\(\color{red}{步骤:}\)

1.先使两个结点跳到同一深度。
2.如果两个结点相等了,那直接返回这个结点
3.否则两个结点一起跳到LCA的子结点位置
4.返回这个位置的父亲,即为LCA

2. 转换为RMQ问题

\(\color{red}{步骤:}\)

1.求出树的dfs序(每个结点进入和每次回溯到的时候都记录)
2.记录每个点第一次进入时在dfs序列中的位置pos[u]
3.两个点u,v的LCA即为dfs序中[pos[u],pos[v]](不妨设pos[u]<=pos[v])中dep最小的结点

3. Tarjan求LCA

\(\color{red}{步骤:}\)
1.记录每个点的询问
2.dfs回溯时用并查集合并(将子结点集合并到父节点集合,以父节点为根)
3.查询lca(u,v)时假设此时u已访问,v刚访问到,那么u的并查集的根即为答案

最小生成树

Kruskal

贪心方法:将边按照边权排序,每一次都只是拿取边权最小的边,看它连接的两个点是否在一个连通块中,这里会用到并查集来维护,如果已经联通了,那么就说明会有更小的边已经连了;反之就加入,并且把两个点联通起来。

最短路径

Dijkstra算法

支持单源最短路,可以使用堆优化,复杂度为O(mlogm),但是不可以去判负环。

SPFA算法

该算法可以判负环,但是日常还是不要用,因为复杂度是错误的。

判负环的方法:

1.用len[u]表示源点到u的最短路中有几条边。
2.在更新最短路长度的时候顺便求出。
3.显然如果没有负环,len [u]<n
4.每一次顶点入队时都要进行check,如果len[u]>=n,则负环存在

Floyed算法

适用于多源最短路,直接三重循环枚举,利用中转站来使得两个点的距离变小。由于比较暴力而且复杂度是O(n^3)所以一般也不会用到。但是该算法也是一种思想,也会用到其他的题目。

建图技巧

通过添加虚拟点等手段将问题转化,从而达到减少边数等目的。

拓扑排序

给定一张有向图,要求输出一个序列,输出是要满足:如果图中u到v有一张有一条有向边,那么序列v在u的后面。如果该序列存在的话,那么就说明一定没有环,因为如果有环的话就不会有入度为0的边。

连通分量

在图中:顶点之间有路径一定互相到达。可以用并查集维护一个连通分量。
可以使用Tarjan算法在O(n+m)的时间内求出。

Tarjan算法

记录两个数组,一个表示没个点被访问的时间,另一个记录搜索树子树的点能访问的点DFN的最小值。

缩点

在图中,由于有强连通分量,并且在强连通分量中的点也都可以很容易的到达,所以我们可以把强连通分量看作为一个点,这样会比较好处理。

差分约束

直观的看,差分约束就是几个不等式,通过不断的相加来获得一个最终的形如x+y<=a的形式,a是一个常数,来获得最大值。我们想要求的最大值,可以通过图论跑最短路来求的。实际上就是x到y的最短路。
\(\color{red}{步骤:}\)
1.将形式转化为xi-xj<=ci的形式
2.对于每一个不等式都是从xj向xi来那一条长为ci的边
3.求出s到t的最短路
如果图中有负环,那么就不存在解;
S到t没有约束,即不能到达,所以是无限大。

环套树/基环树

其实就是树上加上了一条边。
步骤:
1.随意找一个点开始当成树进行dfs,并记录每个结点访问的时间戳dfn
2.dfs的过程中一定会有一个点往dfn比自己小的点连了边,那么这条边可以看成加上的那条。记录下这条边(u,v)
3.暴力让u和v往上爬到根,记录他们分别经过的点。
4.深度最大的他们都经过的点为他们的lca,u->lca之间的所有点+v->lca之间的所有点即构成环。

欧拉图

通过图中所有边一次且仅一次行遍所有顶点的通路称为欧拉通路。
通过图中所有边一次且仅一次行遍所有顶点的回路称为欧拉回路。
具有欧拉回路的图称为欧拉图。
具有欧拉通路的图称为半欧拉图。

圈套圈算法

dfs搜索,不能再往下走(不能重复使用一条边,但可以重复经过一个点)便回溯,回溯时记录路径,回溯时不清除对边的标记,最后求出来的路径就是欧拉回路。

DP

状态与记忆化搜索

状态的本质就是问题的子问题。不论是搜索还是DP都需要设计问题的状态,设计出了状态,就可以找出状态与状态之间的关系,然后再进行推导,状态之间的转移就是状态之间的联系,一般地DP是递推式的形式。状态需要保证无后效性和最优子结构。
动态规划和记忆化搜索都做到了每一个状态只会计算一遍,去掉了冗余计算。记忆化搜索也是搜索的一个有效的剪枝方法,复杂度多半是多项式级别的。记忆化搜索的代码比DP的代码更好理解,代码实现也容易。

线性DP

最基础的DP就是线性DP了吧,一般的话都会是一个for循环求出答案,复杂度一般都是O(n)左右的。学习好线性DP有助于对DP的理解,还是蛮重要的。

多维DP

树形动态规划

树形动态规划是在树上,根据树的拓扑性进行动态规划。我们依旧可以使用原来的思路来解决。

背包动态规划

背包DP主要包含三种问题,01背包,完全背包,多重背包。01背包是每一个物品只能选择一件,并且是倒着枚举;而完全背包是可以选择无限件,是正着枚举的;多重背包是每一个物品有多件,可以随便选择多件。

P4394 选举

按照席位数量从大到小排序,然后再去维护一个01背包。因为题目要满足任意政党退出时,其他政党的席位不得超过一半,所以需要枚举j>sum/2,j-a[i]<=sum/2。

数位DP

数位DP可以解决区间内求满足一定条件的数的个数。我们这里运用了前缀和的思想,在询问[l,r]这个区间时,如果直接枚举是会超时的,所以我们可以用[0,r]-[0,l-1]来获得[l,r]区间的满足条件的数的个数。

状压DP

状压DP,在题目中,我们定义某些状态是很繁琐的,所以需要进行简单化,我们可以用几个简单的数字来进行表示状态,最形象的莫过于二进制数字表示状态。例如在八数码这个题中,我们可以使用到这个思想,来使得状态好表示,本来是一个3*3的矩阵,我们可以用字符串来表示状态,来进行变换。

DP的优化

关于DP的优化,我们最常使用的就是单调队列优化,单调队列可以支持插入一个元素和查询当前队列中的最大值。单调队列优化应用的DP方程多半是f[i]=max(f[l]...f[r])+a[i].我们就可以预处理出f[l]~f[r]中的最大值。

记录:n(1+1/2+1/3+...+1/n)=ln(n)

转载于:https://www.cnblogs.com/handsomegodzilla/p/11605756.html

2019清北学堂学习笔记相关推荐

  1. 清北学堂学习笔记 第一期

    Day 1 1.贪心的奇怪方法:调整法 调整法,顾名思义,就是用别的方式进行题目的分析以及证明,例如说luogu的最大乘积.这种题目的主要分析思路为:先考虑一些简单的情况,通过简单的情况来推出一些有用 ...

  2. 2017国庆 济南清北学堂 8天乐

    Day 0 想吐槽济南的堵车 大约接近4点从莱芜走的,走高速一个多小时,5点左右就到了济南,但是因为济南的堵车,下班高峰期,用了两个多小时才到了我们的目的地.好不容易到宾馆登记了,mc还要我们开会,8 ...

  3. 2020清北学堂秋季营感想——Hoarfrost

    2020清北学堂秋季营感想 前言:九月三十日放假以后,就马不停蹄地开始了这一次的奥赛培训.原先参加过暑假的提高组腾飞营,当时第一场模拟赛拿了第一,便觉得CSP的题目难度不会很高,普及+/提高-左右的难 ...

  4. 清明培训 清北学堂 DAY1

    今天是李昊老师的讲授~~ 总结了一下今天的内容: 1.高精度算法 (1)   高精度加法 思路:模拟竖式运算 注意:进位 优化:压位 程序代码: #include<iostream> #i ...

  5. 【十一日记】 清北学堂酱油记

    Day 0 五点起床 困~ 喜闻乐见的六点集合 嗯我们出发的时候迟到了四分钟( 06:26 第一次出去怎么多人 啧啧啧 @57的熊孩子们好多啊qwq 十分钟到一中 果然老司机233 嗯 目睹他们放学吃 ...

  6. 2019年Java Web学习笔记目录

    Java Web学习笔记目录 1.Java Web学习笔记01:动态网站初体验 2.Java Web学习笔记02:在Intellij里创建Web项目 3.Java Web学习笔记03:JSP元素 4. ...

  7. 清北学堂 2017-10-07

    ********今天得了在清北的最高分,有点开心wwwww,mjy爱您! 树  [问题背景]  zhx 是二叉查找树大师.  [问题描述]  二叉查找树是一种特殊的二叉树(每个节点最多只有两个儿子的树 ...

  8. 【清北学堂】广州OI学习游记

    \(Day~0\) 早上\(9\)点多才爬起来,然后水了道题. 下午从[数据删除]出发,颠簸了将近\(5\)个小时终于抵达广州. 一出地铁站--卧槽这天,卧槽这风,要下雨的节奏? 没过两分钟倾盆大雨. ...

  9. 清北学堂培训2019.4.4

    第一次培训,心情有点激动(尽管没了清明节),还见到了各地的dalao们,十分开森 Day 1(李昊dalao) 上午篇 上午呢,主要讲了关于高精,快速幂,膜模意义下的运算,筛素数,费马小定理以及欧拉定 ...

最新文章

  1. express 设置跨域
  2. linux clock命令,Linux中clock命令起什么作用呢?
  3. 【实验】不会部署VRRP?看完就会了
  4. 网络工程师面试PK--胜者为王
  5. C#——《C#语言程序设计》实验报告——综合练习——委托、Lambda表达式、LINQ、接口
  6. Mysql常用的命令
  7. 可能是最强大的【CSS】动画库
  8. 台式蓝牙模块_华为第一台台式机来了!已上线官网 定价尚未公布
  9. cri-o 与 cni的集成分析
  10. Qt5学习笔记之QQ登录界面三:添加图片资源
  11. String[]与ListString 相互转换 KKK笔记
  12. 干货 | 推荐几款实用的思维导图工具
  13. python 爬虫 代理池
  14. matlab基础学习——基础数学函数(持续更新)
  15. 大数据营销在旅游行业的应用方案
  16. Linux下SD卡开发笔记(一)-SD 相关基础概念
  17. 算法导论第八章思考题
  18. HDU 4382 【矩阵快速幂】【欧拉降幂】
  19. 主板前置USB插线接法大全
  20. linux没有网卡装置,基于Linux的虚拟网卡实现方法、装置、设备及介质与流程

热门文章

  1. Windows8应用生命周期 Metro Style Apps Lifecycle
  2. Vista下将目录的所有者恢复为 TrustedInstaller 的方法
  3. 用XMLHTTP获取动态页生成的HTML内容
  4. 华为荣耀畅玩7c计算机在那,华为荣耀畅玩7C
  5. c语言 long和short区别,5分钟读懂Android 中的toast short 和long的区别
  6. php模板技术smarty,PHP模板技术Smarty
  7. 语言 提取列名_学习健明老师发布的R语言练习题的学习笔记(二)
  8. matlab中fminunc函数使用方法,[分享]无约束非线性规划函数\fminunc函数使用方法(MATLAB)...
  9. 为什么需要消息队列?
  10. java 调用servlet_[Java Servlet]调用 Servlet