LGSpider-HydroOJ/premade-lgdescription.md

214 lines
41 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

## 【入门1】顺序结构
千里之行,始于足下。程序设计虽然花样繁多,但还是要从最简单的地方开始学习,由浅入深,直至掌握。毕竟任何复杂的工程代码都是由一行行简单的代码组成的。
我们编写计算机程序,将一个任务分解成一条一条的语句,计算机会按照顺序一条一条的执行这些语句,这就是顺序结构程序设计。
以上题单的选题来自洛谷编写教材《深入浅出程序设计竞赛 - 基础篇》,并带有详细的教程和讲解,点击下方的图片了解该图书详情。【官方网店绝赞热卖中!】>>>
更有 kkksc03 站长亲自讲授的交互课,配套《深入浅出》使用,亲自在课堂中编写程序,在动手的过程中学习。【点击此处报名!】>>>
## 【入门2】分支结构
人们在人生中需要做出许多选择,小到考虑晚上吃什么,大到决定高考志愿填报的学校。只有一次次选择后才能带来无限可能,我们要根据自己掌握的情况,做出最佳的选择。
程序的执行也不是一成不变的,往往会要求程序能够在不同的场合下有不同的动作。这时就需要在代码中使用条件语句来做出不同的选择。比如说,登录洛谷网时,网站后台会将你提交的用户名和密码在后台数据库中看看是否匹配,如果能够匹配就登陆成功,否则就登陆失败。这一章就来介绍如何让程序做出选择。
以上题单的选题来自洛谷编写教材《深入浅出程序设计竞赛 - 基础篇》,并带有详细的教程和讲解,点击下方的图片了解该图书详情。【官方网店绝赞热卖中!】>>>
更有 kkksc03 站长亲自讲授的交互课,配套《深入浅出》使用,亲自在课堂中编写程序,在动手的过程中学习。【点击此处报名!】>>>
## 【入门3】循环结构
虽然计算机可以在短时间批量处理成千上万条指令,但是不少问题中有许多规律性的重复操作,比如说计算几百个学生的平均分,或者对上万人的名单进行排序。仅使用顺序或者分支结构,对每一步操作都写出对应的语句是不可能的;但可以使用循环语句让计算机反复执行类似的任务。
本章将会介绍循环结构程序设计,同时前面的内容也会进一步的巩固。学完这一章,读者可以初步感受到计算机高效解决问题的能力。
以上题单的选题来自洛谷编写教材《深入浅出程序设计竞赛 - 基础篇》,并带有详细的教程和讲解,点击下方的图片了解该图书详情。【官方网店绝赞热卖中!】>>>
更有 kkksc03 站长亲自讲授的交互课,配套《深入浅出》使用,亲自在课堂中编写程序,在动手的过程中学习。【点击此处报名!】>>>
## 【入门4】数组
计算机运算速度很快,一秒钟可以处理成千上万的数据。之前的例子都是读取一个数据后立刻对这些数据进行处理,然后再也不需要用到这些数据了;有时候,我们读入数据后还需要将这些数据保存下来,便于以后再次使用。如果保存个别几个数据,可以设立几个变量存储;但是如果要存储成千上万个数据,总不能定义成千上万个变量吧。
既然可以通过循环语句来重复执行结构类似的语句,也有办法一次定义一组成千上万个的相同类型的变量——使用数组。这样就可以把大量的数据存储下来,随时使用了。数组不仅可以存储输入的数据,还能存下运算过程中的“半成品”甚至答案,是 C++ 中非常重要的一部分。
以上题单的选题来自洛谷编写教材《深入浅出程序设计竞赛 - 基础篇》,并带有详细的教程和讲解,点击下方的图片了解该图书详情。【官方网店绝赞热卖中!】>>>
更有 kkksc03 站长亲自讲授的交互课,配套《深入浅出》使用,亲自在课堂中编写程序,在动手的过程中学习。【点击此处报名!】>>>
## 【入门5】字符串
计算机并不仅仅能够处理数学问题,还可以用来处理文字,比如写文章、处理代码、记录信息等等……如果需要将各种语句记录在计算机中,就要用到字符串或者字符数组。
我们已经在最开始的地方尝试输出过"I love Luogu" 的字符串,也介绍过单个字符和数字对应的 ASCII 编码。在这一章会介绍字符串的存储和处理的方法。同时也初步接触了 STL这使得可以“站在前人的肩膀上”完成程序简化编程的难度。
以上题单的选题来自洛谷编写教材《深入浅出程序设计竞赛 - 基础篇》,并带有详细的教程和讲解,点击下方的图片了解该图书详情。【官方网店绝赞热卖中!】>>>
更有 kkksc03 站长亲自讲授的交互课,配套《深入浅出》使用,亲自在课堂中编写程序,在动手的过程中学习。【点击此处报名!】>>>
## 【入门6】函数与结构体
有时程序中会使用多次相同的语句,而且无法通过循环来减少重复编程。对于这样的功能,如果能像使用 sqrt()、max() 这样变成一个函数那该多好啊其实每个程序都用到了主函数——main()。除此之外,还可以自己定义其他函数,并将参数喂给这些函数,使其能够根据这些参数完成要求的任务。不过这方面还有更复杂的一些知识点,比如参数传递与变量的作用域,接下来也需要学习。函数内还能调用自己,也就是递归函数,这是程序设计新手入门公认的第一道坎,但却是非常重要的一部分。
最后介绍了结构体,可以建立并操作对象。存储一些和对象有关的信息会变得相当便利。例如,可以设计结构体来存储一位同学的各项信息——姓名、年龄、性别、考试成绩等等,而一个确定的同学就是一个对象。可以很方便地操作一个对象,也可以用数组批量存储对象。
以上题单的选题来自洛谷编写教材《深入浅出程序设计竞赛 - 基础篇》,并带有详细的教程和讲解,点击下方的图片了解该图书详情。【官方网店绝赞热卖中!】>>>
更有 kkksc03 站长亲自讲授的交互课,配套《深入浅出》使用,亲自在课堂中编写程序,在动手的过程中学习。【点击此处报名!】>>>
## 【算法1-1】模拟与高精度
恭喜大家完成了第一部分语言入门,相信大家已经可以使用 C++ 写出一些简单程序了。
各位读者有听说过“建模”一词吗?所谓“建模”,就是把事物进行抽象,根据实际问题来建立对应的数学模型。“抽象”并不意味着晦涩难懂;相反,它提供了大量的便利。计算机很难直接去解决实际问题,但是如果把实际问题建模成数学问题,就会大大地方便计算机来“理解”和“解决”。
举个生活中常见的例子:我们拿到了某次数学考试的成绩单,现在需要知道谁考得最好。当然不能把成绩单对着电脑晃一晃,然后问“谁考得最好?”。需要通过一种途径让计算机来理解这个问题。这个问题可以建模成:“给定数组 score[],问数组内元素的最大值”。这样建模后,就能很方便的写程序解决问题了。对于这个问题,采用之前讨论过的“擂台法”,就可以给出答案。
如何把实际问题建模成数学问题,主要依靠我们的经验和直觉、当然还有你灵动的思维;而算法与数据结构,正是解决数学问题的两把利剑。从这一章开始会介绍一些程序设计竞赛中的一些常见套路算法,而下一部分会介绍基础的数据结构。如果已经认真学习完了第一部分,相信这一部分也不在话下。
这一章是语言部分的延伸,会介绍一些竞赛中会出现的“模拟题目”——这里的“模拟”不是指模拟某场比赛的模拟题,而是指让程序完整的按照题目叙述的方式执行运行得到最终答案。同时也会介绍可以计算很大整数的高精度运算方法。这一章对思维与算法设计的要求不高,但是会考验编程的基本功是否扎实。
以上题单的选题来自洛谷编写教材《深入浅出程序设计竞赛 - 基础篇》,并带有详细的教程和讲解,点击下方的图片了解该图书详情。【官方网店绝赞热卖中!】>>>
## 【算法1-2】排序
在生活中我们经常需要对一些东西排序。比如考试评卷后老师会要求你按照成绩高低将试卷排序,玩扑克牌时要按点数排序手牌,在洛谷刷题将题库按照难度排序然后简单题刷起(友情提示:长期只刷简单题不会有长进的)。多亏了排序过程,可以将无序的杂乱无章的东西整理清楚,便于查询统计和利用。
以上题单的选题来自洛谷编写教材《深入浅出程序设计竞赛 - 基础篇》,并带有详细的教程和讲解,点击下方的图片了解该图书详情。【官方网店绝赞热卖中!】>>>
## 【算法1-3】暴力枚举
设想一下,你觉得家门口的山非常碍事,下决心发扬“愚公移山”精神,凭借一镐一担打算把山一点一点的移走。虽然精神值得褒奖,而且理论上是可行的,只要给予足够多的时间迟早能做到。但是,实际上并不可能给你那么多时间,所以使用这种办法在有生之年是不可能将山移开的(也许你可以使用更好的办法,比如使用魔法或者设法让天神感动,让他帮你移山)。然而,如果你只是把一个不到半人高的小沙堆给移走,那使用这种方法很快就可以完成了。
算法的世界高深莫测,但是很多问题的解决方法简单而粗暴——就是枚举出所有可能的情况,然后判断或者统计,从而解决问题。在很多程序设计比赛中,有许多比较简单的题目是可以通过枚举暴力解决的;而有的更有具有挑战性的题目虽然有更巧妙的解法,但依然可以使用枚举暴力完成部分任务。
本章将介绍一些枚举与暴力策略,这是非常基础而且重要的,但是对初学者来说还是会有一些挑战。请务必理解本章之前的所有章节后再开始本章的学习。
以上题单的选题来自洛谷编写教材《深入浅出程序设计竞赛 - 基础篇》,并带有详细的教程和讲解,点击下方的图片了解该图书详情。【官方网店绝赞热卖中!】>>>
## 【算法1-4】递推与递归
有些目标是宏大的,比如要在 IOI 赛场中得到满分(俗称 AK IOI。如果你现在还是一个普通的学生那么想达成这个目标太难了。但把这样宏大的目标分解为很多个子任务就没觉得那么复杂了。
要想 AK IOI只需要入选国家队参加 IOI 即可。那怎么成为入选国家队呢?参加中国队选拔赛并通过面试答辩即可。使用同样的思路往前倒推,直到最后只剩下最基础的任务(比如认真的读完这章内容并完成练习),做完这样的小任务就很简单了。
像这样将一个很大的任务分解成规模小一些的子任务,子任务分成更小的子任务,直到遇到初始条件,最后整理归纳解决大任务的思想就是递推与递归思想,不过这两者还是有一些区别。
这一章涉及的内容是动态规划思想与分治策略的基础,大家也要认真学习啦,说不定目标就真的达到了。
以上题单的选题来自洛谷编写教材《深入浅出程序设计竞赛 - 基础篇》,并带有详细的教程和讲解,点击下方的图片了解该图书详情。【官方网店绝赞热卖中!】>>>
## 【算法1-5】贪心
如果你想在算法竞赛中得奖,就要尽可能多读书、多思考、多练习。去完成尽可能多数量与种类的算法题目积累知识和经验,在考场上放平心态,就可以达到目标。但因为花了太多时间在编程上而极度压缩休息的时间,反而会效率低下,得不偿失。很多时候太贪婪不是一件好事,因为目光短浅,没有考虑到后面的事情,结果没有办法保证最后的结果做到最好。
在算法竞赛中求解某些问题时,只需要做出在当前看来是最好的选择就能获得最好的结果,而不需要考虑整体上的最优,即使目光短浅也是没有关系的。本章就介绍这样的贪心策略。
以上题单的选题来自洛谷编写教材《深入浅出程序设计竞赛 - 基础篇》,并带有详细的教程和讲解,点击下方的图片了解该图书详情。【官方网店绝赞热卖中!】>>>
## 【算法1-6】二分查找与二分答案
大家还知道怎么在一本很厚的词典查找一个单词吗?字典中的单词是按照“字典序”进行排序的,比如 code<pan<pancake 如果我们要找一个单词就要将字典从中间翻开然后将这面单词跟想要找的单词比较如果这面单词在需要寻找的单词之前就将字典往后翻否则就往前翻直到找到准确的单词为止
大家可以发现越接近需要查询的单词翻动书面的页数就越少你肯定不会从第一页开始一面一面翻逐个查看每个单词是否就是自己想要查的单词这样做就太慢了虽然实际情况不是那么精确但是基本上使用了二分思想”。
如果序列是有序的就可以通过二分查找快速定位所需要的数据除此之外二分思想还能求出可行解的最值问题比如想知道某款手机最高能多少楼高度摔下来而不会摔坏使用二分的方式可以用最小实验次数就能得到结果当然你需要准备好几个样品)。
以上题单的选题来自洛谷编写教材深入浅出程序设计竞赛 - 基础篇》,并带有详细的教程和讲解点击下方的图片了解该图书详情。【官方网店绝赞热卖中!】>>>
## 【算法1-7】搜索
我们在之前的章节介绍了暴力枚举策略,将所有可能的情况都枚举一遍以获得最优解,但是枚举全部元素的效率如同愚翁移山,无法应付数据范围稍大的情形。本章在暴力枚举的基础上介绍了搜索算法,包括深度优先搜索和广度优先搜索,从起点开始,逐渐扩大寻找范围,直到找到需要的答案为止。
严格来说,搜索算法也算是一种暴力枚举策略,但是其算法特性决定了效率比直接的枚举所有答案要高,因为搜索可以跳过一些无效状态,降低问题规模。在算法竞赛中,如果选手无法找到一种高效求解的方法(比如贪心、递推、动态规划、公式推导等),使用搜索也可以解决一些规模较小的情况;而有的任务就是必须使用搜索来完成,因此这是相当重要的策略。
以上题单的选题来自洛谷编写教材《深入浅出程序设计竞赛 - 基础篇》,并带有详细的教程和讲解,点击下方的图片了解该图书详情。【官方网店绝赞热卖中!】>>>
## 【数据结构1-1】线性表
我们之前已经研究过很多算法了!比如,二分法可以用来“给定单调函数,求零点”;冒泡排序可以用来“给定一个数组,将其排序后输出”……你已经知道算法很有用,接下来要学习的数据结构,也一样很有用!初学数据结构,您可能会觉得无从下手;不过不用担心,本书会用很多生活中实际存在的例子来解释这些数据结构。
比如,在商场里面排队结账,或者在网上“秒杀”商品,差别很大;但它们都遵循着相同的规则——讲“先来后到”。早来的,就早买到商品;晚来的,就晚买到商品,甚至可能买不到商品。可以利用“先来后到”这一规则,把这两种排队模式统一起来——它们都是“队列”,都可以用队列这一数据结构来模拟。然后建模,编写计算机程序解决这些问题。
这一章,先开始学习线性表。线性表是最简单、最基本的一种数据结构,一个线性表示多个具有相同类型数据“串在一起”,每个元素有前驱(前一个元素)后继(后一个元素)。根据不同的特性,线性表也分为栈、队列、链表等等。因为这些特性,数据结构可以解决不同种类的问题。
以上题单的选题来自洛谷编写教材《深入浅出程序设计竞赛 - 基础篇》,并带有详细的教程和讲解,点击下方的图片了解该图书详情。【官方网店绝赞热卖中!】>>>
## 【数据结构1-2】二叉树
我们之前介绍了线性表这一类数据结构,并且学习了如何使用线性表解决一类特定的问题(数据具有明显的前后关系,可以进行线性连接)。本章将介绍一类新的数据结构——二叉树。
看看窗外的橡树吧。一般来说,树有一个粗壮的树干,再往上面树干就会分成两叉或者多叉,接着树枝会继续一直分下去,一直分到末端的叶子为止(不过也有可能是花或者果子)。
如想统计一棵苹果树上面有多少苹果,只需要知道树杈左边的苹果数量和树杈右边的苹果数量,然后计算它们的和就行了。至于树杈左边有多少个苹果?可以使用一样的方法来统计,把这个分枝当作树干,然后统计这个树干的左分杈和右分杈的苹果数量和……直到统计到树枝末端每一个苹果,然后依次汇总就可以得到苹果的数量 。
很明显,树形结构不仅能表示数据间的指向关系,还能表示出数据的层次关系,而有很明显的递归性质。因此,我们可以利用树的性质解决更多种类的问题。
以上题单的选题来自洛谷编写教材《深入浅出程序设计竞赛 - 基础篇》,并带有详细的教程和讲解,点击下方的图片了解该图书详情。【官方网店绝赞热卖中!】>>>
## 【数据结构1-3】集合
有时候,我们并不关心数据之间的前后关系,也不关心数据的层次关系。一些确定元素只是单纯的聚集在一起,这样的元素聚集体被称为集合。
当希望知道某个数据是否存在一个集合中,或者两个元素是否在同一个集合中时,就需要使用一些集合数据结构来维护集合元素之间的关系。
以上题单的选题来自洛谷编写教材《深入浅出程序设计竞赛 - 基础篇》,并带有详细的教程和讲解,点击下方的图片了解该图书详情。【官方网店绝赞热卖中!】>>>
## 【数据结构1-4】图的基本应用
我们已经学过一些简单的数据结构,例如线性表和二叉树。现在,需要学习一种新的数据结构——图。虽然相比于前面讲过的数据结构,图会复杂一些,但是依然能用很多生活中存在的例子来解释图这种数据结构。
比如,现在新同学站在校园的正门口,手里拿着校园地图。可以从地图上看到有很多建筑物。复杂的路网四通八达,连接着这些建筑物。如果希望偷个懒,走最近的道路到达目的地,或者是希望制定一种方案,参观完学校内的每一种建筑物,都可以使用“图”这一数据结构来模拟。通过建模,编写计算机程序,就可以解决这类问题。
以上题单的选题来自洛谷编写教材《深入浅出程序设计竞赛 - 基础篇》,并带有详细的教程和讲解,点击下方的图片了解该图书详情。【官方网店绝赞热卖中!】>>>
## 【数学1】基础数学问题
根据计算机的物理构造,数字信号由 0低电位和 1高电位组成。因此所有类型的数据包括数字、文字、图片、音频等都在计算机中以 0 和 1 进行储存和运算。这就是需要了解二进制和其他进制原理的原因。
这个世界纷繁复杂,有无数的选择与决策,带来了各种各样的结果。我们在日常生活中经常会遇到这样的问题:知道了事物所有的选项和决策,最后有多少种可能的结果呢?为了能够解决这些问题,我们需要学习基本计数原理的知识,包括加法与乘法原理、排列与组合等等,解决一些简单的计数问题,并能了解计数与现实生活之间的联系。
我们前面已经学过一些简单的组合数学,并了解了简单的计数方法。如果把组合数学理解成对于计数问题的研究,那么数论数学就是对于整除性问题的研究,其中组合与数论是程序设计竞赛中的常见考点。
以上题单的选题来自洛谷编写教材《深入浅出程序设计竞赛 - 基础篇》,并带有详细的教程和讲解,点击下方的图片了解该图书详情。【官方网店绝赞热卖中!】>>>
## 【算法2-1】前缀和、差分与离散化
荀子曰:不积跬步,无以至千里;不积小流,无以成江海。这句话揭示了世间万物整体和部 分之间的关系——庞大的整体由若干个体组成;单个个体虽小,但经过一点一滴的累积,也能聚 成一个庞大的整体。学习工作贵在不断积累,不断充实、丰富、完善自己,所有的努力都会有回 报。
在算法竞赛中,利用整体和部分的性质可以达成很多目的,例如利用前缀和可以在常数时间 复杂度中查询区间和,利用差分在常数时间复杂度对序列进行区间操作,或者利用离散化去除无 用数据区间,通过放缩保留有用的数据。这些操作使得我们不必每次都要重复处理个体的数据, 而是直接对整体进行处理,从而降低时间复杂度,提升运算效率。这一章将会学习前缀和、差分 和离散化的思想,并使用这些思想完成一些简单的任务。
该题单内容将继续改进。
对应进阶篇第 2 章。
## 【算法2-2】常见优化技巧
对应进阶篇第 1 章。
评价算法的标准,除了正确性以外,最重要的就是程序的运行效率是否足够高,是否可以在 限定的时间内处理完指定的数据。在“暴力枚举”一章中介绍了可以通过剔除无效操作来提升算 法效率。除此之外,还可以用“单调性”和“空间换时间”来优化时间复杂度。将这两种思想结 合,产生了单调栈和单调队列等工具。
这些技巧可以帮助我们排除无效选项,在一定范围内保持单调性,常常可以将一些问题求解 的时间复杂度降到
O(n)
O(n),因此称为线性时间复杂度优化。
## 【算法2-3】分治与倍增
如果想知道我国的人口数量,就需要进行人口普查。让每一个省份都去统计本省有多少人, 然后将各省人口累加起来,就可以获得全国的人口数量。而要想知道某一个省的人口数量,可以 让省里的每一个城市统计本市有多少人,然后将各市人口累加起来,就可以获得这个省的人口数 量……以此类推,层层细分,最后统计一个村子或者一个小区有多少人,这个任务就很简单了。 把一个复杂的问题细分成若干结构相同但规模更小的子问题,然后将每个子问题的解合并起来, 就得到了复杂问题的解,就是本章将会介绍的分治策略。
本章的最后还介绍了倍增算法。将一个较大的数字分为若干个
2^i
2
i
的和,同时维护所有长度为
2^i
2
i
的区间,可以解决一些特定的问题,例如区间求最值的问题。
对应进阶篇第 3 章。
## 【算法2-4】字符串
对应进阶篇第 7 章。
字符串,就是由字符连接而成的序列。我们可以使用不同的算法来解决不同的和字符串有关 的问题。《基础篇》中介绍过的哈希表,可以将一个字符串映射为一个整数,可以高效地查询这 个字符串是否存在于一个集合中。本章介绍另外两种比较基础的字符串工具——Knuth-Morris-Pratt 算法和字典树。
Knuth-Morris-Pratt 算法,简称 KMP 算法,可以高效的从一个字符串中查询另外一个指定的 字符串是否存在;如果存在的话位置在哪里。它会利用字符串匹配过程中失败的信息,从而减少 字符串查找比较的次数。
而字典树,又称为 Trie 树,可以方便的储存、索引和查找字符串。当然除了字符串,还可以 将数字拆成二进制,存入字典树中,常常可以高效处理涉及到异或的问题。
## 【算法2-5】进阶搜索
搜索算法可以枚举所有可能的结果,找到最优结果或者统计符合要求的结果数量。我们在 《基础篇》中介绍了搜索算法包括深度优先搜索DFS和广度优先搜索BFS。在搜索的过 程中,每个过程都有若干决策,所以搜索是一类时空开销都极大的枚举算法,其时间复杂度往往 是指数或者是阶乘级别的。
为了使运行速度快一点,在这一章将介绍一些更优秀的搜索策略,也许可以帮助我们通过更 多的测试点。然而,就算是用上了这些策略,这些搜索算法的时间复杂度往往还是指数级别的。 当实在没能想出更好的解法时,可以考虑使用搜索算法来通过一些数据规模较小的测试点。
该题单内容将继续改进。
对应进阶篇第 4 章。
## 【数据结构2-1】二叉堆与树状数组
本章将介绍两种树形结构——二叉堆和树状数组。
大学生活是忙碌而丰富多彩的,有大量的任务需要完成,例如作业、实验、完成论文、兼职 工作等。这些任务都是不得不做的,而且每个任务都有对应的截止日期。对于一个有拖延症的学 生来说,他每次决定完成哪些任务,一定会选择最接近截止日期的任务,因为这个任务最为紧 迫,优先级最高。
对于这种可以查询最大或最小值,可以插入新的元素,可以删除最大最小值的集合容器,被 称为优先队列。通过使用二叉堆实现优先队列,可以较高效率地完成插入和查询操作。
而树状数组可以很方便的维护序列的前缀和等信息。因此,可以使用树状数组高效完成单点 修改、区间求和的任务。虽然之后介绍的线段树也可以完成这些任务,而且时间复杂度都是
O(logn)
O(logn),但是树状数组运行时间的常数更小,所需空间更小,代码也更加短小精悍。
## 【数据结构2-2】线段树
如何快速求出一个序列的区间和呢?可以使用前缀和。如何快速求出一个序列的最值呢?可 以使用 ST 表。这两种数据结构在建立的时候颇费功夫,但使用的时候效率很高。如果再增加一 个需求:时不时的修改序列的值,那么这两种数据结构就无法高效完成了。不过可以使用线段树 来解决这类问题。
在基础篇中,我们已经学习了二叉树的有关概念。而线段树是一种特殊的二叉树,它可以将 一个线性的序列组织成一个树状的结构,从而可以在对数的时间复杂度下访问序列上的任意一个 区间并进行维护。在本章中,将学习线段树的构建方法和一些简单的应用。
该题单内容将继续改进。
对应进阶篇第 6 章。
## 【图论2-1】树
树结构广泛存在于我们的日常生活中,刻画了一种广泛存在的事物关系。某家公司的一个部 门可能有许多员工,但是他们只会有一个直属的中层领导;而每个部门的中层领导,只会有一个 直属的上司——总经理。这种组织架构就是树形结构。如果同一部门的两个员工发生了矛盾,可 以找部门的中层领导裁决,因为这两名员工都是这个领导的下属;但两个不同部门的员工发生了 矛盾,就有可能需要提请总经理裁决了。
在《基础篇》的“二叉树”一章中已经对树形结构有了初步的认识。树结构是一种常见的数 据组织形式,体现的关系是一种“一对多”的关系。本章会进一步对树形数据结构的一些性质进 行探究,并且探讨树上的一些特殊的关系。
该题单内容将继续改进。
对应进阶篇第 9 章。
## 【图论2-2】最短路
最短路是图论中最经典的模型之一,在生活中也有很多应用。举个例子,我国幅员辽阔,有 很多个城市。这些城市之间由许多高速公路相连接。想从上海出发前往北京,有很多种路径,如 何选择一条最短的路径就是最基本的最短路问题。有时候问题会更加复杂,加上别的限制条件, 例如某些城市拥有服务区,连续开车达到一定的时间就必须要进服务区休息,或者是某些路段在 特定的时间内会堵车,需要绕行等等,这需要更加灵活地使用最短路算法来解决这些问题。
除了解决给定图的最短路问题,最短路模型还可以解决许多看起来不是图的问题。如果解决 一个问题的最佳方案的过程中涉及很多状态,这些状态是明确的且数量合适,而且状态之间可以 转移,可以考虑建立图论模型,使用最短路算法求解。本章介绍了多种适用于不同情况的最短路 算法,可以根据实际情况选择合适的算法。
该题单内容将继续改进。
对应进阶篇第 10 章。
## 【图论2-3】最小生成树
试想在一个平原国家中有若干城市,要建立若干高速公路,这些高速公路可以建成直线,连 接在两个城市之间。如何规划这些高速公路使建造距离总和最短,就是一种典型的最小生成树问 题。
树是一种特殊的图,具有很多特殊的性质。生成树问题研究的是将图中的所有顶点保留,但 只选择图中的部分边,得到一棵树(也就是图的生成树)的问题。最小生成树则是在这些生成树 中,边权之和最小的生成树。可以使用
Prim 算法或者
Kruskal 算法求解最小生成树。
对应进阶篇第 11 章。
## 【图论2-4】连通性问题
对应进阶篇第 12 章。
在之前的章节中已经讨论过最短路问题和生成树问题了。在无向图中,连通性问题可以使用 并查集解决。但是当删去某个点或某条边时,如何判断图是否连通?对于无向图来说,有一些点 或者是边是“关键”的,没有这些点或者边,这个图就会分裂成各个部分。
无向图中连通的点可以相互到达,那么有向图上可以相互到达的点对有什么性质呢?有序性 是一个很好的性质,但对于多数错综复杂的有向图来说并不能直接进行拓扑排序。可以将一些可 以互相到达的点合并成一个点,使这个有向图变为有向无环图,即可进行拓扑排序。
本章会分别介绍无向图的双连通性和有向图的强连通性。
## 【动态规划1】动态规划的引入
对应进阶篇第 13 章。
我们接触过很多“求最佳方案”的题目了。遇到这种问题,可以考虑使用贪心算法在每步决 策时使用单步最佳方案,或者使用枚举(搜索)算法枚举所有可能的决策。使用贪心算法的问题 是“目光短浅”,不一定可以站在全局的角度来统筹决策,可能会出现结果并不是全局最佳的; 而使用枚举搜索算法,虽然可以遍历所有的可能,但如果数据量稍大,就可能超时。这个时候可 以考虑使用动态规划解决这些问题。
动态规划的核心是“状态缓存”,将一个状态的统计结果传递到另外一个状态,这有一点类 似递推算法。动态规划是算法竞赛的常客,非常考验选手问题分析和代码实现能力,因此值得我 们在本书中专门开辟一个部分来介绍。
## 【动态规划2】线性状态动态规划
对应进阶篇第 14 章。
我们在前一节初步介绍了动态规划。本节将会介绍动态规划中最常规的一类题型——线性状 态动态规划,以及其变种问题。
顾名思义,线性状态动态规划指的是一类状态定义与题设内容线性相关的动态规划。题设中 有序列(数组),那动态规划的状态就是一维的;如果题设中有棋盘(网格)、那么动态规划的 状态就是二维的……线性状态动态规划定义状态的时候经常会考虑“某类有序事件中前若干个子 事件的答案”。
一般来说线性的状态最为直观容易用有向无环图DAG表示也比较容易理解。所以 遇到动态规划问题构造状态时,通常会最优先考虑能否采用线性状态。
## 【动态规划3】区间与环形动态规划
在前一节学习了线性动态规划后,本章将会讨论两个稍显复杂模型:区间动态规划,及其变 种环形动态规划。这两类动态规划的特点是,囿于问题本身限制,“某类有序事件中前若干个事 件的子答案”不再能支撑状态转移,我们需要去寻找“从某个元素起到另一个元素结束所包含所 有的(连续)元素的子答案”作为状态。
可以想象,在这个描述下,每个状态会对应于原题序列上的一个区间。区间具有良好的性 质:短的区间可以拓宽成长的区间,相同长度的区间互相不包含。这样,可以把所有状态理解成 DAG即不会有可以互相到达的局面。基于这个原则可以思考如何构造转移并在下列例题中 深入理解区间与环形动态规划。
## 【动态规划4】树与图上的动态规划
对应进阶篇第 16 章。
树与图上的动态规划,顾名思义,就是以树或图为模型的动态规划。树上动态规划是最常见 的动态规划形式之一,因为树型本身就带来了子结构(树上的父子关系),大部分情况下,都是 通过子结点的 DP 值推导出父结点的 DP 值。而图上的动态规划,由于没有很明显的子结构,所 以比较受限。有向无环图DAG由于点之间有着明显的分层性质相较于图更加良好。许多图 上的问题可能转化后成为树上的或 DAG 上的问题。剩下的许多问题模型,又可以转化为最短路 问题。我们将依次介绍上述提到的模型。
## 【动态规划5】状态压缩动态规划
对应进阶篇第 17 章。
在《基础篇》的“暴力枚举”一章中介绍了子集枚举,使用二进制数字来表示一种状态。对于“状态压缩动态规划”中的状态,通常与集合相关联。集合本身具有三个性质:确定性,互异性和无序性 。这也就决定了只关心每个元素的存在状态,而这通常可以使用 0 或者 1 表示存在 或者不存在。例如,一顿饭有 8 道菜,小明同学最终吃了某几道菜,必然是这 8 道菜的子集。令 1 表示吃了, 0 表示没吃那么以下几个状态10000001 表示只吃了 1 号菜和 8 号菜; 01010101 表示吃了 2 , 4 , 6 , 8 四道菜。
由此可见,可以通过一串 01 码来清晰地表示一个集合的状态。同时,在确定了最低位的前 提下,一串 0-1 码与一个二进制数一一对应。这种表示状态的方式被称为状态压缩,简称状压。所谓“压缩”,即是用一个二进制数来保存一组状态,将一个集合的状态压缩进了一个二进制数中,而通常这个二进制数在十进制下的大小可以作为其编号。状态压缩本质上是进行了两步操作:
给这个集合的每个状态一个编号;
通过该编号,轻易地访问该状态。
这一章将会从状态压缩谈起,描述一些简单的应用,并最终落脚到状态压缩应用最广泛的动态规划题目。
## 【数学2-1】进阶数论
对应进阶篇第 19 章。
自文明的产生起就,人类就一直和整数打交道了。对于整数来说,可以进行加减乘除这样的 四则运算。整数之间的加、减、乘运算,其结果还是整数;但是遇到了除法,就可能产生余数。 人们在研究整数的过程中,发现了越来越多数字的性质,例如整除性,并在此基础上出现了质数 与合数的概念。进一步,数学家探索了更多的与整数的性质,并将其发展成为数学的一个重要分 支——数论。数论有许多有趣和复杂的数学规律,被称为数学上的皇冠。
在《基础篇》的“整除理论”一章中已经研究过质数与合数、公约数与公倍数等概念。本章 内容将更加深入地研究整数的性质,包括同余、逆元、同余方程、线性筛和欧拉函数等。本章内 容会有些抽象,读者需要花费一些精力理解这些概念。
## 【数学2-2】组合数学与计数
对应进阶篇第 20 章。
在《基础篇》中已经介绍了计数原理与排列组合相关的问题,可以帮助求解在各种场合下的方案数。有一些概念需要复习一下:
加法原理:做一件事,有
n 类方法第一类方法中有m1种方法第二类有m2种……第
n 类有mn种则完成这件事有\sum_{i=1}^n m_i∑i=1nmi种不同的方案。
乘法原理做一件事有n 个步骤需要依次完成第一步中有m1种方法第二步有m2种……第 n 步有 mn 种,则完成这件事有\prod_{i=1}^n m_i∏i=1nmi种不同的方案。
排列数:从
n 个人里面选出
m 个人站成一排,考虑这些人的相对顺序,方案数是:
A_n^m=n\cdot (n-1)\cdots(n-m+1) = \frac{n!}{(n-m)!}Anm=n⋅(n1)⋯(nm+1)=(nm)!n!。
组合数:从
n 个人里面选出
m 个人,不考虑这些人的相对顺序,方案数是
C_n^m=\frac{A_n^m}{m!}=\frac{n!}{m!\cdot (n-m)!}Cnm= m! An m = m!⋅(nm)!n!。
这一章将会介绍更多和计数和排列组合相关知识,可以解决更多的方案数量统计的问题。
## 【数学2-3】概率与统计
对应进阶篇第 21 章。
在日常生活中,有些事情是必然的——例如太阳从东方升起;有些事情是偶然的——例如明 天下雨。随着对偶然事件的研究不断深入,我们发现其中很大一部分是有章可循的。抛 100 次硬 币,大概有 50 次左右是正面朝上的;掷 600 次骰子,其中大概有 100 次得到点数 1。
本章讨论的概率论,就是研究这些随机现象中蕴含的数学规律。概率论是实用的科学:尽管 人们无法预知明天是否真的会下雨,但掌握了明天下雨的概率后,至少可以作出“要不要带伞出 门”的决策;当计算出一场赌局的期望收益是负数时,还是不要参与赌局为宜。
本章节先讨论一些非常基础的概率模型(例如古典概型、几何概型),然后给出其形式化的 定义。接下来,讨论全概率公式、贝叶斯公式,并引入数学期望的概念。
## 【数学2-4】基础线性代数
对应进阶篇第 22 章。 线性代数是关于向量和线性映射的一个数学分支,主要处理线性关系问题。虽然读者可能没 有系统的地学习过线性代数,但肯定已经和线性代数打过交道了:在数学课中,我们学习过如何 求解二元一次方程,以及如何对一个函数进行伸缩变换;在物理课中,我们学习过力的平行四边 形法则,也知道力作用在物体上,并使物体在力的方向上通过了一段距离的过程就是做功。这些 问题都可以抽象成线性代数,相信读者有能力理解基础的线性代数。
许多读者学习线性代数的时候过于重视概念和运算,而忽视了其几何意义,这会让读者觉得 线性代数是晦涩难懂的东西。同时,线性代数是一个很大的话题,在有限的篇幅全部介绍清楚是 不现实的。本节内容将结合线性代数的几何意义介绍线性代数基本概念及其在算法竞赛中的应 用,帮助读者初步理解线性代数。
## 【数据结构2-3】线段树的进阶用法
本章主要学习可持久化线段树与树状数组套权值线段树的一些应用。我们知道,对于一棵线 段树而言,如果它的总长度不变,那么它的形态是不会改变的。也就是说,在值域不变情况下, 权值树的形态是不会改变的。这样一来,就可以对权值树进行合并的操作。对于权值树
A
B
A
B 形态相同,则可以合并这两棵权值树,合并的方式就是对应结点相加。 显然,合并后的树依然是一棵权值线段树。可以将权值线段树的合并类比为加法。类似的, 也可以类比得到权值线段树的减法,即对应点数相减。这个性质非常的重要,接下的内容就要基 于这一性质。本章内容较难,读者可以将本章的阅读的优先级调后。
## 【动态规划6】动态规划的设计与优化
对应进阶篇第 18 章。
动态规划作为算法竞赛中出现频率高,且难度上限很高的话题,在状态设计以及转移的优化 上有大量的技巧。如果读者尚不能熟练构造状态,请相信熟能生巧:在大量的练习下,自然而然 就能理解如何构造状态以及如何在不同的状态之间转移。除此之外,使动态规划算法变得更加高 效也是一个重要的目标。本章将介绍如何设计动态规划,并介绍了一些动态规划的优化案例。