C++从入门开始讲算法(1)

China☆狼群 提交于 2020-03-09 12:12:57

以下内容来自于洛谷网站

本文章仅是讲一个目录,细节会在之后的文章中出现

大家请拿起手中的纸和笔,认真做笔记!

1.顺序结构

千里之行,始于足下。程序设计虽然花样繁多,但还是要从最简单的地方开始学习,由浅入深,直至掌握。毕竟任何复杂的工程代码都是由一行行简单的代码组成的。

我们编写计算机程序,将一个任务分解成一条一条的语句,计算机会按照顺序一条一条的执行这些语句,这就是顺序结构程序设计。

2.分支结构

人们在人生中需要做出许多选择,小到考虑晚上吃什么,大到决定高考志愿填报的学校。只有一次次选择后才能带来无限可能,我们要根据自己掌握的情况,做出最佳的选择。

程序的执行也不是一成不变的,往往会要求程序能够在不同的场合下有不同的动作。这时就需要在代码中使用条件语句来做出不同的选择。比如说,登录洛谷网时,网站后台会将你提交的用户名和密码在后台数据库中看看是否匹配,如果能够匹配就登陆成功,否则就登陆失败。这一章就来介绍如何让程序做出选择。

3.循环结构

虽然计算机可以在短时间批量处理成千上万条指令,但是不少问题中有许多规律性的重复操作,比如说计算几百个学生的平均分,或者对上万人的名单进行排序。仅使用顺序或者分支结构,对每一步操作都写出对应的语句是不可能的;但可以使用循环语句让计算机反复执行类似的任务。

本章将会介绍循环结构程序设计,同时前面的内容也会进一步的巩固。学完这一章,读者可以初步感受到计算机高效解决问题的能力。

4.数组

计算机运算速度很快,一秒钟可以处理成千上万的数据。之前的例子都是读取一个数据后立刻对这些数据进行处理,然后再也不需要用到这些数据了;有时候,我们读入数据后还需要将这些数据保存下来,便于以后再次使用。如果保存个别几个数据,可以设立几个变量存储;但是如果要存储成千上万个数据,总不能定义成千上万个变量吧。

既然可以通过循环语句来重复执行结构类似的语句,也有办法一次定义一组成千上万个的相同类型的变量——使用数组。这样就可以把大量的数据存储下来,随时使用了。数组不仅可以存储输入的数据,还能存下运算过程中的“半成品”甚至答案,是 C++ 中非常重要的一部分。

5.字符串

计算机并不仅仅能够处理数学问题,还可以用来处理文字,比如写文章、处理代码、记录信息等等……如果需要将各种语句记录在计算机中,就要用到字符串或者字符数组。

我们已经在最开始的地方尝试输出过"I love Luogu" 的字符串,也介绍过单个字符和数字对应的 ASCII 编码。在这一章会介绍字符串的存储和处理的方法。同时也初步接触了 STL,这使得可以“站在前人的肩膀上”完成程序,简化编程的难度。

6.函数与结构体

有时程序中会使用多次相同的语句,而且无法通过循环来减少重复编程。对于这样的功能,如果能像使用 sqrt()、max() 这样变成一个函数,那该多好啊!其实每个程序都用到了主函数——main()。除此之外,还可以自己定义其他函数,并将参数喂给这些函数,使其能够根据这些参数完成要求的任务。不过这方面还有更复杂的一些知识点,比如参数传递与变量的作用域,接下来也需要学习。函数内还能调用自己,也就是递归函数,这是程序设计新手入门公认的第一道坎,但却是非常重要的一部分。

最后介绍了结构体,可以建立并操作对象。存储一些和对象有关的信息会变得相当便利。例如,可以设计结构体来存储一位同学的各项信息——姓名、年龄、性别、考试成绩等等,而一个确定的同学就是一个对象。可以很方便地操作一个对象,也可以用数组批量存储对象。

7.排序

在生活中我们经常需要对一些东西排序。比如考试评卷后老师会要求你按照成绩高低将试卷排序,玩扑克牌时要按点数排序手牌,在洛谷刷题将题库按照难度排序然后简单题刷起(友情提示:长期只刷简单题不会有长进的)。多亏了排序过程,可以将无序的杂乱无章的东西整理清楚,便于查询统计和利用。

8.高精度算法与模拟

各位读者有听说过“建模”一词吗?所谓“建模”,就是把事物进行抽象,根据实际问题来建立对应的数学模型。“抽象”并不意味着晦涩难懂;相反,它提供了大量的便利。计算机很难直接去解决实际问题,但是如果把实际问题建模成数学问题,就会大大地方便计算机来“理解”和“解决”。

举个生活中常见的例子:我们拿到了某次数学考试的成绩单,现在需要知道谁考得最好。当然不能把成绩单对着电脑晃一晃,然后问“谁考得最好?”。需要通过一种途径让计算机来理解这个问题。这个问题可以建模成:“给定数组score[],问数组内元素的最大值”。这样建模后,就能很方便的写程序解决问题了。对于这个问题,采用之前讨论过的“擂台法”,就可以给出答案。

如何把实际问题建模成数学问题,主要依靠我们的经验和直觉、当然还有你灵动的思维;而算法与数据结构,正是解决数学问题的两把利剑。从这一章开始会介绍一些程序设计竞赛中的一些常见套路算法,而下一部分会介绍基础的数据结构。如果已经认真学习完了第一部分,相信这一部分也不在话下。

这一章是语言部分的延伸,会介绍一些竞赛中会出现的“模拟题目”——这里的“模拟”不是指模拟某场比赛的模拟题,而是指让程序完整的按照题目叙述的方式执行运行得到最终答案。同时也会介绍可以计算很大整数的高精度运算方法。这一章对思维与算法设计的要求不高,但是会考验编程的基本功是否扎实。

9暴力枚举

设想一下,你觉得家门口的山非常碍事,下决心发扬“愚公移山”精神,凭借一镐一担打算把山一点一点的移走。虽然精神值得褒奖,而且理论上是可行的,只要给予足够多的时间迟早能做到。但是,实际上并不可能给你那么多时间,所以使用这种办法在有生之年是不可能将山移开的(也许你可以使用更好的办法,比如使用魔法或者设法让天神感动,让他帮你移山)。然而,如果你只是把一个不到半人高的小沙堆给移走,那使用这种方法很快就可以完成了。

算法的世界高深莫测,但是很多问题的解决方法简单而粗暴——就是枚举出所有可能的情况,然后判断或者统计,从而解决问题。在很多程序设计比赛中,有许多比较简单的题目是可以通过枚举暴力解决的;而有的更有具有挑战性的题目虽然有更巧妙的解法,但依然可以使用枚举暴力完成部分任务。

本章将介绍一些枚举与暴力策略,这是非常基础而且重要的,但是对初学者来说还是会有一些挑战。请务必理解本章之前的所有章节后再开始本章的学习。

10 递归递推

有些目标是宏大的,比如要在 IOI 赛场中得到满分(俗称 AK IOI)。如果你现在还是一个普通的学生,那么想达成这个目标太难了。但把这样宏大的目标分解为很多个子任务,就没觉得那么复杂了。

要想 AK IOI,只需要入选国家队,参加 IOI 即可。那怎么成为入选国家队呢?参加中国队选拔赛并通过面试答辩即可。使用同样的思路往前倒推,直到最后只剩下最基础的任务(比如认真的读完这章内容并完成练习),做完这样的小任务就很简单了。

像这样将一个很大的任务分解成规模小一些的子任务,子任务分成更小的子任务,直到遇到初始条件,最后整理归纳解决大任务的思想就是递推与递归思想,不过这两者还是有一些区别。

这一章涉及的内容是动态规划思想与分治策略的基础,大家也要认真学习啦,说不定目标就真的达到了。

11.贪心

如果你想在算法竞赛中得奖,就要尽可能多读书、多思考、多练习。去完成尽可能多数量与种类的算法题目积累知识和经验,在考场上放平心态,就可以达到目标。但因为花了太多时间在编程上而极度压缩休息的时间,反而会效率低下,得不偿失。很多时候太贪婪不是一件好事,因为目光短浅,没有考虑到后面的事情,结果没有办法保证最后的结果做到最好。

在算法竞赛中求解某些问题时,只需要做出在当前看来是最好的选择就能获得最好的结果,而不需要考虑整体上的最优,即使目关短浅也是没有关系的。本章就介绍这样的贪心策略。

12.二分

大家还知道怎么在一本很厚的词典查找一个单词吗?字典中的单词是按照“字典序”进行排序的,比如 code<pan<pancake 。如果我们要找一个单词,就要将字典从中间翻开,然后将这面单词跟想要找的单词比较。如果这面单词在需要寻找的单词之前,就将字典往后翻,否则就往前翻,直到找到准确的单词为止。

大家可以发现,越接近需要查询的单词,翻动书面的页数就越少。你肯定不会从第一页开始一面一面翻,逐个查看每个单词是否就是自己想要查的单词,这样做就太慢了。虽然实际情况不是那么精确,但是基本上使用了“二分思想”。

如果序列是有序的,就可以通过二分查找快速定位所需要的数据。除此之外,二分思想还能求出可行解的最值问题,比如想知道某款手机最高能多少楼高度摔下来而不会摔坏,使用二分的方式可以用最小实验次数就能得到结果(当然你需要准备好几个样品)。

13。搜索

我们在之前的章节介绍了暴力枚举策略,将所有可能的情况都枚举一遍以获得最优解,但是枚举全部元素的效率如同愚翁移山,无法应付数据范围稍大的情形。本章在暴力枚举的基础上介绍了搜索算法,包括深度优先搜索和广度优先搜索,从起点开始,逐渐扩大寻找范围,直到找到需要的答案为止。

严格来说,搜索算法也算是一种暴力枚举策略,但是其算法特性决定了效率比直接的枚举所有答案要高,因为搜索可以跳过一些无效状态,降低问题规模。在算法竞赛中,如果选手无法找到一种高效求解的方法(比如贪心、递推、动态规划、公式推导等),使用搜索也可以解决一些规模较小的情况;而有的任务就是必须使用搜索来完成,因此这是相当重要的策略。

14.线性表

我们之前已经研究过很多算法了!比如,二分法可以用来“给定单调函数,求零点”;冒泡排序可以用来“给定一个数组,将其排序后输出”……你已经知道算法很有用,接下来要学习的数据结构,也一样很有用!初学数据结构,您可能会觉得无从下手;不过不用担心,本书会用很多生活中实际存在的例子来解释这些数据结构。

比如,在商场里面排队结账,或者在网上“秒杀”商品,差别很大;但它们都遵循着相同的规则——讲“先来后到”。早来的,就早买到商品;晚来的,就晚买到商品,甚至可能买不到商品。可以利用“先来后到”这一规则,把这两种排队模式统一起来——它们都是“队列”,都可以用队列这一数据结构来模拟。然后建模,编写计算机程序解决这些问题。

这一章,先开始学习线性表。线性表是最简单、最基本的一种数据结构,一个线性表示多个具有相同类型数据“串在一起”,每个元素有前驱(前一个元素)后继(后一个元素)。根据不同的特性,线性表也分为栈、队列、链表等等。因为这些特性,数据结构可以解决不同种类的问题。

15.二叉树

我们之前介绍了线性表这一类数据结构,并且学习了如何使用线性表解决一类特定的问题(数据具有明显的前后关系,可以进行线性连接)。本章将介绍一类新的数据结构——二叉树。

看看窗外的橡树吧。一般来说,树有一个粗壮的树干,再往上面树干就会分成两叉或者多叉,接着树枝会继续一直分下去,一直分到末端的叶子为止(不过也有可能是花或者果子)。

如想统计一棵苹果树上面有多少苹果,只需要知道树杈左边的苹果数量和树杈右边的苹果数量,然后计算它们的和就行了。至于树杈左边有多少个苹果?可以使用一样的方法来统计,把这个分枝当作树干,然后统计这个树干的左分杈和右分杈的苹果数量和……直到统计到树枝末端每一个苹果,然后依次汇总就可以得到苹果的数量 。

很明显,树形结构不仅能表示数据间的指向关系,还能表示出数据的层次关系,而有很明显的递归性质。因此,我们可以利用树的性质解决更多种类的问题。

16.集合

有时候,我们并不关心数据之间的前后关系,也不关心数据的层次关系。一些确定元素只是单纯的聚集在一起,这样的元素聚集体被称为集合。

当希望知道某个数据是否存在一个集合中,或者两个元素是否在同一个集合中时,就需要使用一些集合数据结构来维护集合元素之间的关系。

17.图的基本应用

我们已经学过一些简单的数据结构,例如线性表和二叉树。现在,需要学习一种新的数据结构——图。虽然相比于前面讲过的数据结构,图会复杂一些,但是依然能用很多生活中存在的例子来解释图这种数据结构。

比如,现在新同学站在校园的正门口,手里拿着校园地图。可以从地图上看到有很多建筑物。复杂的路网四通八达,连接着这些建筑物。如果希望偷个懒,走最近的道路到达目的地,或者是希望制定一种方案,参观完学校内的每一种建筑物,都可以使用“图”这一数据结构来模拟。通过建模,编写计算机程序,就可以解决这类问题。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!