前期准备
1.有足够的刷题量(最好200+)
2.锻炼自己的调试能力
3.最好参加模拟赛
4.安排:周日(知识点+例题,取自真题);周六(扩展+练习)
5.在参考时间内AC(从开始写到AC的时间),练熟练度
6.强调,算法一定要落实到代码的具体实现上
由数据范围反推算法复杂度以及算法内容
一般ACM或者笔试题的时间限制是1秒或2秒。
在这种情况下,C++代码中的操作次数控制在 10$^7$∼10$^8$最佳。
题目中的数据范围可以给我们很大的提示!!!
注:
- O(logn)一般指$\log_2n$.
- int范围约是2*10$^9$.
- long long的范围约是10$^{18}$.
下面给出在不同数据范围下,代码的时间复杂度和算法该如何选择:
- n≤30, 指数级别, dfs+剪枝,状态压缩dp
- n≤100 => O(n$^3$),floyd,dp,高斯消元
- n≤1000 => O(n$^2$),O($n^2logn$),dp,二分,朴素版Dijkstra、朴素版Prim、Bellman-Ford
- n≤10000 => O(n∗$\sqrt n$),块状链表、分块、莫队
- n≤100000 => O($nlogn$) => 各种sort,线段树、树状数组、set/map、heap、拓扑排序、dijkstra+heap、prim+heap、spfa、求凸包、求半平面交、二分、CDQ分治、整体二分
- n≤1000000 => O(n), 以及常数较小的 O($nlogn$) 算法 => 单调队列、 hash、双指针扫描、并查集,kmp、AC自动机,常数比较小的 O($nlogn$)的做法:sort、树状数组、heap、dijkstra、spfa
- n≤10000000 => O(n),双指针扫描、kmp、AC自动机、线性筛素数
- n≤10$^9$=> O($\sqrt n$),判断质数
- n≤10$^{18}$ => O($logn$),最大公约数,快速幂(接近long long范围)
- n≤10$^{1000}$=> O($(logn)^2$),高精度加减乘除
- n≤10$^{100000}$=> O($logk×loglogk$),k表示位数,高精度加减、FFT/NTT
作者:yxc
链接:https://www.acwing.com/blog/content/32/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
一 递归与递推
1.关于scanf/printf
与cin/cout
的比较:
cin/cout
速度稍慢,当数据范围< 10$^5$时用;
scanf/printf
速度巨快,当数据范围>= 10$^5$时用。
2.递归(dfs)
dfs也即深度优先搜索。
所有递归问题都能画出一棵递归搜索树,方便分析。
常用数字最好记一下:
2.1 acwing.92. 递归实现指数型枚举
从 1~n 这 n 个整数中随机选取任意多个,输出所有可能的选择方案。
输入格式
输入一个整数n。
输出格式
每行输出一种方案。
同一行内的数必须升序排列,相邻两个数用恰好1个空格隔开。
对于没有选任何数的方案,输出空行。
本题有自定义校验器(SPJ),各行(不同方案)之间的顺序任意。
数据范围
1≤n≤15
输入样例:
1 | 3 |
输出样例:
1 | 3 |
y总思路:
从数据范围推断时间复杂度约为O(2$^n$)/O(n*2$^n$)。
递归最重要的是顺序,要把所有方案不重复不遗漏的找出来。
先画图:
注意边界问题:
再考虑代码实现:
1 | // soluition 1,y总题解,直接输出排列 |
2.2 acwing.94.递归实现排列型枚举
把 1~n 这 n 个整数排成一行后随机打乱顺序,输出所有可能的次序。
输入格式
一个整数n。
输出格式
按照从小到大的顺序输出所有方案,每行1个。
首先,同一行相邻两个数用一个空格隔开。
其次,对于两个不同的行,对应下标的数一一比较,字典序较小的排在前面。
数据范围
1≤n≤9
输入样例:
3
输出样例:
1 | 1 2 3 |
1思路:
顺序1:依次枚举每个数放在哪个位置;
顺序2:依次枚举每个位置放哪个数。
2画出递归搜索树(以顺序2为例):
3代码实现:
1 | // y总题解 |
u代表层数(也就是放数的第n个位置),n代表分支数。搜索完一个分支,再搜索另一个分支。递归其实就是函数调用,你手动模拟一下函数的执行过程就清楚了。
4时间复杂度分析:
(作为扩展内容,蓝桥杯不要求掌握,但以后面试会用到)
第一层:枚举分支,有一个for循环,复杂度为n;
第二层:有n个函数,每个函数有一个for循环,复杂度为n*n;
第三层:填完一个位置后,还有n-1个位置,复杂度为n*(n-1)*n
;
…
最后一层:每个叶子节点输出方案,有一个for循环,复杂度为n!*n
。
加起来,通过不等式放缩证明:n! <= (1+n+n(n-1)+n(n-1)(n-2)+...+n!) <= 3*n!
,因为括号外层还有个n,所以最终时间复杂度为$O(n*n!)$