ACM算法笔记

HNU君陌

此笔记为本人在准备ACM校赛时阅读资料所整理的总结心得,适合入门级的同学,仅供参考:

第一部分语言

第一章程序设计入门
1.1算术表达式
整数型用printf()时为%d,实数用%f输出,保留一位n数为printf(“%.nf”, )
1.2变量及其输入
整数型用scanf()时为%d,实数用%lf输入,%5d表示按5位数打印,%05d表示按5位数表示,不足补0。
占位符和变量的数据类型一一对应,且每个变量前均需要加”&”符号。
竞赛时,选手程序的执行是自动完成的,没有人工干预,不需要界面友好。也不要让程序按任意键退出:system(“pause”)或添加一个多余的getchar()
不要在竞赛中使用getch()、getche()、gotoxy()、clracr()函数
在算法竞赛中,每行输出均应以回车符结束,包括最后一行,除非特别说明,每行的行首不应有空格,但行末可以有多余空格。另外,输出的每两个数或者字符串之间应以单个空格隔开。即算法竞赛的程序制作三件事:读入数据,计算结果,打印输出
1.3变量交换
三变量法:
t=a; a=b; b=t;
单变量法(只有定义了加减法的数据类型才可以):
a=a+b; b=a-b; a=a-b;
1.4分支结构程序设计

第二章循环结构设计
2.1 for循环
尽量缩短变量的定义范围。
浮点运算可能存在误差,在进行浮点数比较的时候,应该考虑到误差。
2.2 while循环
int的范围是32位整数,范围是-2147483648~2147483647
可以在计算过程中将小的变量类型转换为大的变量类型防止乘法溢出,在输出时再转换
2.3 循环的代价
可以使用time.h和clock()函数获得程序的运行时间
2.4 输入输出框架
变量在未赋值之前的值是不确定的,不一定等于0
文件输入输出:在main函数开头加入freopen(“input.txt”,”r”,stdin);和freopen(“output.txt”,”w”,stdout);使得scanf从文件input.txt输入,printf写入文件output.txt。并在结尾的最后加上fclose()关闭两个文件。
先声明变量fin和fout,吧scanf改成fscanf,第一个参数为fin
在算法竞赛中,选手应严格遵守比赛的文件名规定,包括程序文件名和输入输出文件名,不要弄错大小写,不要拼错文件名,不要使用绝对路径或相对路径。
在多数据题目中,一个常见的错误是:计算完一组数据后某些变量没有重置,影响到下一组数据的求解。解决方法为:将变量定义在循环中
当嵌套的两个代码块中有同名变量的时候,内层的变量会屏蔽外层变量,有时会引起错误。

第三章数组和字符串
3.1 数组
在算法竞赛中,常常难以精确定义需要的数组大小,可以定义的很大。
对于变量n,n++和++n都会给n加1,但当他们用在一个表达式中时,n++会使用加1前的值计算表达式,++n会使用加1之后的值计算表达式
只有将数组的个数a定义在main外面的时候才可以定义的很大,数组才可以开得很大,否则会导致异常退出。比较大的数组尽量声明在main函数之外
memset(数组名,字符,截止大小);函数的作用是数组初始化,也在string.h中定义。
3.2 字符数组
输入字符的时候用scanf(%S),不要加上&,如果是字符数组可以scanf(%s, s[i]),注意遇到空白字符会停下来
strchr(字符,字符数组)的作用是在一个字符串中查找单个字符
strlen(字符数组名)作用是获得字符数组的实际长度
strcpy(a,b)用来复制(赋值)(string可以直接用=),strcmp(a,b)用来比较(string可以直接用==比较),strcat(a,b)用来连接(string可以直接用加号来连接)
在算法竞赛中不可以使用gets,建议用getline或者getchar或者scanf
十进制可以理解为123=((1*10)+2)10+3,同理,二进制可以理解为101=((12)+0)*2+1

第四章函数和递归
4.1 自定义函数和结构体
在算法竞赛中,请总是让main函数返回值为0
为了使用方便,往往用typedef struct{域定义;}类型名;的方式定义一个新类型名。域定义即为结构体中的数据类型。
即使最终答案在所选择的数据类型范围之内,计算的中间结果仍可能溢出
建议把为此(用来判断某事物是否具有某种特性的函数)命名成“is_xxx”的形式,返回int值,1为真0为假
4.2函数调用与参数传递
尽量减少指针的使用量,可能的情况下,尽量使用全局变量
以数组为参数调用函数时,实际上只有数组首地址传递给了函数,需要另加一个参数表示元素个数。
4.3 递归
注意为递归函数编写终止条件,否则将产生无限递归
每次执行完指令,均需要一层判断来判断是否终止递归。

第五章 STL入门
5.1 stl初步
STL为c++的标准模板库。
sort(开始地址,终止地址,排序规则);函数可用来做数组排序,如果没有第三个参数时实现的是从小到大的排序,如果想要修改,需要自定义一个bool类型函数,并将函数名作为第三个参数,头文件为algorithm
lower_bound(开始地址,终止地址,比较数值);函数用来查找大于或等于比较数值的第一个数值的地址,头文件为algorithm
vector<数组类型>数组名;声明一个不定长的某类型数组,可以用数组名.clear()清空,数组名.resize()改变大小,数组名.push_back()和数组名.pop_back()在尾部添加和删除函数,用empty()测试是否为空,数组名.size()得到大小,头文件是vector
5.2 大整数类
定义一个结构体,包括用vector定义的不定长整型数组vector <数据类型> 数组名,构造函数,赋值运算符(对等号运算符进行重载,要有两个,分别对long long形参和string形参,返回值均是指针),输入输出运算符(对>>和<<进行重载)。头文件要包括iostream,vector和string。

第二部分 基础篇

第六章 数据结构基础
6.1 栈和队列

6.2 链表
在数组中频繁移动元素是很低效的,可以使用链表
为了方便起见,常常在链表的第一个元素之前放一个虚拟结点
在双向链表这样复杂的链式结构中,往往会编写一些辅助函数来设置连接关系,如果数据结构上的某个操作很耗时,有时候可以用加标记的方式处理,而不需要真的执行那个操作。但同时,该数据结构的所有其他操作都要考虑这个标记。
测试的任务就是检查一份代码是否正确,如果找到了错误,最好还能提供一个让他出错的数据;调试的任务是找到错误原因并改正,两者要交替进行
测试数据结构程序的常用方法是对拍:写一个功能相同但速度较慢的简易版本,再写一个数据生成器,不停比对快慢两个程序的输出。简易版本的代码越简单越好,因为重点不在效率,而在于正确性
6.3 树和二叉树
给定一颗包含2^d个节点(d为树的高度)的完全二叉树,如果把结点从上到下从左到右编号为1,2,3…则k结点的左右子节点编号分别为2k和2k+1
如果要定义一颗二叉树,一般是定义一个“结点”类型的结构体,然后保存树根的指针
可以用new运算符申请空间并执行构造函数
宽度优先遍历:用队列实现二叉树的层次遍历
如果程序动态申请内存,请注意内存泄漏。程序执行完毕后,操作系统会回首改程序申请的所有内存(包括泄露的)
可以用数组来实现二叉树,方法是用整数表示结点编号,left[u]和right[u]分别表示u的左右子节点的编号
可以用静态数组配合空闲列表来实现一个简单的内存池
二叉树有三种深度优先遍历:先序遍历,中序遍历,后序遍历
给定二叉树的中序遍历和后序遍历,可以构造出二叉树。方法是根据后序遍历找到树根,然后在中序遍历中找到树根,从而找出左右字数的节点列表,然后递归构造左右字数
6.4 图
图也有DFS遍历和BFS遍历,其中前者用递归实现,后者用队列实现
很多复杂的迷宫问题都可以转化为最短路问题,然后用BFS求解,在套用BFS框架之前,需要先搞清楚图中的“结点”包含的内容
使用BFS求出图的最短路后,可以用递归方法打印最短路的具体路径,如果最短路非常长,递归容易引起栈溢出,可以用循环和vector来保存路径
可以用DFS求出有向无环图(DAG)的拓扑排序,如果排序失败说明该有向图存在有向环,不是DAG
根据连通性和度数可以判断出无向图和有向图是否存在欧拉道路和欧拉回路,可以用DFS构造欧拉回路和欧拉道路。

第七章 暴力求解法
7.1 简单枚举
采用暴力求解法,对问题进行一定的分析往往会让算法更简洁搞笑
7.2 枚举排列
如果某问题的解可以由多个步骤得到,而每个步骤都有若干种选择(这些候选方案皆可能会依赖于先前做出的选择),且可以用递归枚举法实现,则它的工作方式可以用解答树来描述
枚举排列的常见方法有两种:递归枚举和STL中的next_permutation
7.3 子集生成
在枚举子集的增量法中,需要使用定粗的技巧,避免同一个集合枚举两次
可以用二进制表示子集,其中从右往左第i位(从0开始编号)表示元素i是否在集合中(1表示在0表示不在)
当二进制表示子集时,位运算中的按位与、或、异或对应集合的交、并和对称差
7.4 回溯法
当把问题分成若干步骤并递归求解时,如果当前步骤没有合法选择,则函数将返回上一级递归调用,将这种现象称为回溯
如果在回溯法中使用了辅助的全局变量,要及时把他们恢复成原状,特别的,如果函数有多个出口,则需要在每个出口回复被修改的值
在回溯法中应注意不必要的判断
在最优解的问题中应尽量考虑最优性剪枝
7.5 路径寻找问题
路径寻找问题可以归结为隐式图的遍历,他的任务是找到一条从初始状态到最终状态的最优路径,而不是像回溯法那样找到一个符合某些要求的解
隐式图遍历需要用一个节点查找表来判重,一般来说使用STL集合实现的代码最简单但效率最低,如果对时间要求很高,可以先把STL的集合办的程序调试通过,然后转化为哈希表甚至完美哈希表
7.6 迭代加深搜索
对于可以用回溯法求解但解答数深度没有明显上线的题目,可以考虑使用迭代加深搜索

第三部分 竞赛篇

第八章 高效算法设计
8.1 算法分析初步
统计程序中的基础操作的数量,可以排除机器速度的的影响,衡量算法本身的优劣程度
基本操作的数量往往可以写成关于输入规模的表达式,保留最大项并忽略系数后的简单表达式称为算法的渐进时间复杂度,用于衡量算法中基本操作数岁规模的增长情况
在算法设计中,常常不进行精确分析,而是假定各种最坏情况同时取到,得到上界
分治算法一般分三步:划分问题,将实例划分成子问题;递归求解,递归求解子问题;合并问题,合并子问题的解得到原问题的解
在算法分析中,往往可以忽略除法结果是否为整数,而直接按照实数除法分析
8.2 排序与搜索
归并排序:把序列分成元素个数尽量相等的凉拌,把两半元素分别排序,再把两个有序表合成一个,即每次只需要将两个序列的最小元素进行比较,删除其中的较小元素并加入合并后的新表即可
快速排序:把数组的各个元素重拍后分成左右两部分,使得左边的任意元素都小于或等于右边的任意元素,把左右两部分分别排序

你可能感兴趣的:(算法设计)