随缘复习初赛
常识&参考资料
- 初赛
- 史上最全NOIP初赛知识点
- 【全】CSP 初赛通过指南
- CSP-J/S 初赛知识点整理
- CSP-J/S初赛复习(1)-计算机基本常识、进制与编码!
- CSP初赛知识点考前整理
- CSP初赛知识点梳理
二进制
比较恶心的是小数部分
树上问题
二叉树有个性质:叶子节点的个数比度数为 2 的节点多 1
三种遍历顺序
前序、中序、后序,前中后指的是“根”
- 前序遍历:“根”左右
- 中序遍历:左“根”右
- 后序遍历:左右“根”
已知【前序遍历和中序遍历】求【后序遍历】
已知:前序遍历为 FBACDEGH
,中序遍历为 ABDCEFGH
,求后序遍历
解:
前序遍历中,第一个是根节点:F
中序遍历中,找根节点,隔开:ABDCE,F,GH
明确子树的节点个数:5、2
前序遍历中,根据节点个数,区分子树:
- 左:BACDE
- 右:GH
递归到子问题,程序实现如下:
int A[10005]; // 前序遍历
int B[10005]; // 中序遍历
void work(
int L, int R,
int l, int r
) {
/*
A[L]~A[R] 前序遍历
B[l]~B[r] 中序遍历
*/
if (L>R) return; // 边界:空树
int root = A[L];
// TODO : 搜索,使 B[k]=root
int sizel = (k-1)-l+1;
int sizer = r-(k+1)+1;
// 递归左子树
work(L+1, L+sizel, l, k-1);
// 递归右子树
work(l+sizel+1, R, k+1, r);
// 输出:左、右、根,即后序遍历
cout << root << endl;
return;
}
已知【前序遍历和后序遍历】求【中序遍历】
已知:前序遍历 FBACDEGH
,后序遍历 ADECBHGF
,求有多少树满足条件?
分析:根节点绝对固定,无用,删除。当删去根节点后的剩余部分只有一个元素时,它作为刚刚删去的根节点的左右孩子对遍历结果无影响,产生 2 种情况,其余都只有 1 种情况
解:
对于整棵树,根节点:F,没用,删除根节点:
- 前序遍历:BACDEGH
- 后序遍历:ADECBHG
剩下前序遍历中,第一个必是左子树的根节点,后序遍历中,根据 B 划分,确定子树节点数,再根据节点数切割前序遍历
- 前序遍历:BACDE,GH
- 后序遍历:ADECB,HG
该步划分唯一,方案数 = 左右子树情况数的乘积
递归到子问题:二层左子树
删除根节点:B
- 前序遍历:ACDE
- 后序遍历:ADEC
根据 A 划分后序遍历,确定子树节点数,根据节点数切割前序遍历
- 前序遍历:A,CDE
- 后序遍历:A,DEC
该步划分唯一
二层左子树的左子树唯一,看右子树
删除根节点:C
根据 A 划分后序遍历,确定子树节点数,根据节点数切割前序遍历
- 前序遍历:D,E
- 后序遍历:D,E
该步划分唯一
递归到子问题:二层右子树
删除根节点:G
此时只剩 H,位置不确定,产生了 2 种方案
前缀、中缀、后缀表达式
三种表达式的转换
运算符作根节点,左右是运算结果,形成表达式树
然后按照三种方式遍历就行了
图上问题
- 强联通:有向图上,点两两之间可以互相到达
- 弱连通:有向图转换为无向图后联通
- 连通无向图最大边数
- 最短路
- 欧拉路:遍历所有边
- 充要条件:有且只有两个奇点
- 欧拉回路:遍历所有边并回到起点
- 充要条件:没有奇点
- csp2022-s1T9:
个点全排列, 个点都可以作为起点,并且可以向左或向右走,所以方案数为
- 哈密尔顿路:遍历所有点
排序相关
基于比较的排序:冒泡、选择、插入、快排、归并、堆排
其他排序:桶排序、计数排序(按值域开桶)、基数排序(按照十进制位比较)
稳定性:相同元素排序后相对顺序不变,跳着交换的排序都是不稳定的
基于比较的排序比较
算法 | 平均时间 | 最坏时间 | 额外空间 | 稳定性 |
---|---|---|---|---|
冒泡 | × | √ | ||
选择 | × | × | ||
插入 | × | √ | ||
快排 | × | × | ||
归并 | √ | √ | ||
堆排 | × | × |
排列组合
排列组合的计算
被卡西欧宠坏了
常见计数方法
特殊优先、插空法、隔板法
线性丢番图方程
- 正整数解的个数(隔板法):
- 非负整数解的个数(转换+隔板法):
特殊数列
卡特兰数
合法括号序列方案数、栈的输出方案数、
第二类斯特林数
时间复杂度分析
具体知识点看大佬的知乎,下面列几道做过的题
2021 csp-s1 T12
不加记忆化计算斐波那契数列的时间复杂度是指数级的。
递归树叶子结点的计算结果是
而【算法的用时】和【过程中
一种分析方法是,对于递归树,从根节点开始,每个节点都会向下延伸两个子节点,一共
另一种分析方法是,叶子结点的数量是
(这种分析方法得到的结果应该为
答案为 C。
2021 csp-s1 T17
作为一道阅读程序题,在分析复杂度的时候往往会被一堆看不懂的东西唬住。
读懂程序含义能帮助理解一堆抽象的变量名,还有不明所以的加法重构。
这题的特殊性在于,solve1()
和 solve2()
可以立刻猜想是两个功能一致但实现不同的函数。
进一步确认,两个程序需要传入相同的参数 (h,m)
,都取了 h
和 m
的中点 j
,都进行了递归,到这里已经可以基本验证猜想了。
所以这里重构的加法应该和 44~52 行的一些运算对应,结果应该是一样的。
当然读不懂也问题不大,只需要关注运算的数量级就行了,几步不明所以的加法只需要知道它是
接下来来到分析复杂度的重点,对【递归函数】的处理。
对 solve1()
来说,它和 2021 csp-s1 T12 一样,是一个【从叶子节点回溯,不断做加法】的过程,可以抽象为
对 solve2()
来说就有所不同,它在回溯时还扫描了区间 [h,m]
,【不能】简单抽象为不断累积的过程。
这里有两种考虑方法。
第一种是从结果上考虑,也就是【它到底扫描了哪些东西】。递归树根节点的那一次访问扫描了区间 [1,n]
,向下递归出来的两个子区间各自扫描了区间 [1,n/2]
和 [(n/2)+1,1]
,还是相当于扫描了区间 [1,n]
,因此递归树每一层,都相当于对区间 [1,n]
进行了一次扫描,而区间长度每次都除以 2,因此这棵递归树一共有
第二种是写出它的【用时函数表达式】,和上一种方法的分析一样,每一层花 [1,n]
,然后递归为两个子区间,各自的规模是 (然鹅主定理背不下来)
2020 csp-s1 T7
考察基本算法的时间复杂度。
深度优先遍历的过程中,每个节点被访问到之后,会通过与其相连的【边】看看子节点是否被访问过,这一过程可以视作【边】被访问了一次。
由于 vis[]
数组的记录,每个【节点】只会被访问一次。
综上,每条【边】和每个【点】都会被访问一次,所以时间复杂度是
注意平时在表示线性复杂度时常常用
2020 csp-s1 T14
考察基本算法的时间复杂度。
Dijkstra 算法的流程为,从未确定 dis[u]
最小的那一个,然后对其所有出边进行一次松弛操作。
每个【点】都要被选中一次,复杂度
故总时间复杂度为
2020 csp-s1 T17
#include <iostream>
#include <cstdlib>
using namespace std;
int n;
int d[10000];
int find(int L, int R, int k) {
int x = rand() % (R - L + 1) + L;
swap(d[L], d[x]);
int a = L + 1, b = R;
while (a < b) {
while (a < b && d[a] < d[L])
++a;
while (a < b && d[b] >= d[L])
--b;
swap(d[a], d[b]);
}
if (d[a] < d[L])
++a;
if (a - L == k)
return d[L];
if (a - L < k)
return find(a, R, k - (a - L));
return find(L + 1, a - 1, k);
}
int main() {
int k;
cin >> n;
cin >> k;
for (int i = 0; i < n; ++i)
cin >> d[i];
cout << find(0, n - 1, k);
return 0;
}
读懂程序后分析 swap()
的执行次数会更方便。
这段程序是快速排序的改版,用于找到序列中第
采用分治法,由于快排每次会将序列分为两端,左边比指定的数
通过左区间长度可以推算出第
本题中还会出现针对【随机数】的【平均复杂度】和【最坏复杂度】的概念,对于初赛来说,只需要进行复杂度的【估算】,因此可以不用管“平均”具体是谁和谁平均,每次选的随机数不要太极端就可以。而最坏复杂度可以无脑选最坏情况进行计算。
第三题,当 d[i]
递增的时候,每一轮的 swap()
只会做一次,此时时间复杂度就等于递归层数,这样估算的结果是
但官方答案是
第四题,当 d[i]
递减时,相当于第一轮的 swap()
每遇到一对 (a,b)
都会做一次,
举例 7 6 5 4 3 2 1
,取 d[x]=2
,第一轮的变化过程为:
6 7 5 4 3 2 1
6 1 5 4 3 2 7
6 1 5 4 3 2 7
此时可以发现,第一轮做完之后数组处于乱序状态。
接着进到左右子区间中的一个后,最坏情况是依然递减,此时会操作
所以操作次数可以估算为
第六题,如果所有数都一样,那么 a
不会向右移动,b
一路向左和 a
重合,复杂度
此时左右区间大小极度不均衡,每次向下递归,左子区间的大小只有
因此时间复杂度为
结语
2023-09-15,CSP 2023 S1,++RP!!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现