算法入门(C++)

iostream,这个头文件里有很多常用的函数,比如swap交换两个变量的值,max求两个值的最大值等。

cstdio头文件,这个头文件里包含C风格的输入输出。如果你之前学习过C++语言应该知道cin读入和cout输出的方法。那么我们为什么还要用C的scanf和printf呢?因为scanf和printf要比cin和cout的效率高得多。

using namespace std 如果没这行的话,swap就要写成std::swap,代码写起来会麻烦些。

main函数它的返回值是int类型的。那么它到底返回的值是干什么的呢? 它是用来标记程序是否正确无错误返回的,在执行编译好的程序的时候会获得这个返回值。返回0意味着程序无错误退出。

输入格式: 每组数据第一行输入一个数N,表示一共有多少个正方形; 第二行输入N个正整数,表示每个正方形的边长。 如果N=0则输入结束。

输出格式: 每组输出一个 值,表示该组所有正方形的总面积。

while (scanf("%d",&n),n)循环读入n,直到n为0时退出循环。这种输入格式在算法题目中非常常见。逗号表达式,整个表达式的结果就是用逗号分隔的最后一个表达式的值。当n的值为0时,整个表达式的值就是0,此时退出循环。

只引用一个头文件#include <bits/stdc++.h>而无需引用两个头文件。这种并不是所有平台都适用的,用之前记得查阅平台的FAQ。

输入要抄的原文内容,可能有的读者会想写scanf("%s", str);但在这里是行不通的,因为文章的内部会有空格,而这样的语句只能把空格之前的内容读入到str。因此我们要使用读取一整行的函数,如下:gets(str);多提醒一句,使用gets的时候如果发生溢出的话程序是不会报错的,所以在使用gets的时候一定要小心!

scanf函数的返回值:当scanf正确读入时,返回值是和读取的变量数目相等的。而当读取出错、读到文件末尾时,scanf函数会返回-1.while (scanf("%d", &cnt) != -1)

输入数据的范围最大是10000,10000的立方是10^12,而int类型的最大值是2147483647,大约是2*10^9,远远超过了int的最大值,这样在计算过程中就会出现溢出的情况,导致结果错误。因此我们需要用更大的整数类型long long,它的最大值大约为10^19,即使10000个10^12也不会超过long long的取值范围。

程序设计竞赛,顾名思义就是写代码的比赛。现在的程序设计竞赛有解题竞赛、性能竞赛、创意竞赛等各式各样的比赛。

ACM-ICPC,即国际大学生程序设计竞赛,是由美国计算机协会(ACM)主办的面向全球大学生的规模最大的程序设计竞赛。竞赛由三人组队,在5小时内完成8-12道题目,按照通过题目的数量和解决问题的累计时间综合排序决定最终名次。每通过一道题目会发给队伍一个气球。ACM-ICPC(简称为ACM)竞赛由区域赛和总决赛组成,其中区域赛由网络预赛和现场赛组成。网络预赛获得靠前名次的学校可以获得现场赛的名额,通常 每个学校会有1-3个名额,和学校的历史成绩、当场网络赛排名等有关。目前大陆地区的现场赛前15名队伍获得金牌,第16-45名获得银牌,前60%的余 下队伍获得铜牌。其中获得金牌名次靠前的学校可以代表中国大陆参加世界总决赛。世界总决赛共有4枚金牌,4枚银牌和4枚铜牌。大陆的队伍在总决赛的历史成绩出色,其中上海交通大学获得过3次冠军,浙江大学获得过1次冠军。

Topcoder(topcoder.com)是一个举办在线编程比赛的网站,上面有很多比赛类型,常见的有SRM(Single Round Match),Marathon(持续时间很长、且只有一道题,没有固定解的题目),TC Open等。我们接下来着重介绍SRM,也就是个人算法编程竞赛。用户需要下载指定的客户端(Arena下载链接) 才能参加SRM比赛,Topcoder也是唯一一个必须下载客户端才能参加的程序设计竞赛。用户在Topcoder会有积分(rating)和积分对应的 颜色标示,如上图中所示,红>黄>蓝>绿>灰>白。积分最高的几个选手的id左侧会有一个类似靶心的标志,我们称之为 target。ACRush(楼天城)就是target选手之一。

Topcoder(SRM) 每场比赛会按照用户赛前的rating将选手分为div1和div2各自比赛。比赛分为coding阶段、challenge阶段和system test阶段。每个division会有几十个room,每个room内的选手在challenge阶段可以互相查看对方代码,发现错误后可以提交测试数 据进行挑战,挑战成功(对方程序计算结果与正确输出不一致)后可加分,失败则会扣分。这也就是大家口中常说的"cha人",非常刺激。Arena的有趣之处还在于可以安装各式各样的插件(插件列表),这里推荐给你一个大家最常用的插件:ExampleBuilder,它的文档连接在这里。使用这个插件之后能够非常迅速的在本地编写、调试代码。

Google Code Jam(code.google.com/codejam)是谷歌举办的每年一次的世界规模的程序设计竞赛,参赛者要在2-3小时内解决大约4道题。从在线的几轮比赛中胜出后就能够参加现场(Onsite)总决赛。该赛事的特点是每道题都备有Small和Large两组输入数据。即便是难度系数较大的问题,只要输入规模足够小,依然可以简单地求解。另外Google-Code-Jam(通常简称为GCJ)并不在服务器上自动执行程序,而是要求将源代码和本地执行的结果一同提交。

在ACM竞赛的队伍中,队员的角色一般可分为队长、解题核心以及辅助等;比赛中需要完成的任务一般可分为读题、准备、编程和查错等。队长主要工作是统筹规划,制定队伍的计划并作出决策,包括比赛场上以及场下的训练一个优秀的辅助对队伍起着非常重要的作用,稳定的查错能力、出色的出数据能力、细致的读题能力能够让团队的实力提升好几个档次。队友之间的互相了解是一切的基础。队友之间要清楚地知道擅长什么,不擅长什么,将每道题分配给最适合的人做。良好的配合基于队友之间的相互信任,并且队员之间能够准确地向队友表达自己的意思。这些可以通过训练慢慢培养。

IDE(Integegrated Development Environment),中文翻译为集成开发环境,对于ACM初学者来说使用IDE要比使用一个编辑器再使用编译器进行编译方便的多。ACM竞赛中常用的语言为C/C++或Java。C/C++最常用的IDE是CodeBlocks,Java最常用的IDE为Eclipse。需要注意的是下载Windows版本的Codeblocks一定要下载带mingw的,否则需要自己配置C++的编译器,对于初学者来说可能会比较麻烦。 mingw是Minimalist GNU on Windows的缩写,顾名思义是运行在Windows上的GNU库,用来编译C/C++代码。

在 Linux上与mingw对应的是gcc和g++,由于这些都是系统自带的程序,在安装Codeblocks的时候不需要再单独安装一遍了。两个编译器在 一些细微之处还是有不同的,比如64位整型变量的输入(scanf)和输出(printf),在Linux下参数为%lld,而Windows下的参数 为%I64d。比如HDU(acm.hdu.edu.cn)和CF(codeforces.com)就是典型的Windows下搭建的OJ,不过大部分OJ还是Linux环境运行的。在提交用到64位整型的代码时一定要注意平台的操作系统,一般可以通过查看OJ的FAQ或在比赛现场咨询裁判获知。

网上有一些被称为Online Judge(简称OJ)的系统,他们能够自动评测以往程序设计竞赛中的算法题目,在OJ上可以进行练习。此外一些OJ也会定期举办自己的比赛,不妨经常参加一下。

输入格式:依次读入数据,直到文件结束。那么这种输入要怎么处理呢?下面我来给出各个语言的读入方法。

首先是C/C++:while(scanf("%d",&n) != -1){}   或  while(cin >> n){}

Java:Scanner cin = new Scanner(System.in);  while(cin.hasNext()){}

Python:for line in stdin:

Codechef(codechef.com)是印度人创办的OJ,有两种比赛形式,一种是Challenge,持续10天;一种是lunchtime,持续3小时。

Codeforces(codeforces.com)是俄罗斯创办的OJ,会定期举办算法比赛,比赛分为div1和div2,有一套积分和颜色系统,红色最高,红名用户拥有很多特权。

HDU(acm.hdu.edu.cn)是杭州电子科技大学维护的OJ,每年暑期会组织大批高校进行ACM训练,称为多校联合训练,是国内规模最大的ACM校际训练。

什么是代码能力?简单地说,如果你觉得学会了某一个算法或者数据结构,你能否在半个小时(或一小时)内完成程序,并且经过很少的调试一次通过?如果做不到 的话,只能说你还没彻底掌握这个算法或数据结构,要不断地训练,直到达到要求,只有这样才能保证在高度紧张的比赛中敢于实现而不犯错误。

写代码不是在解题,而是实现早已在脑子里准备好的一个逻辑流程。

在设计满足问题要求的算法时,复杂度的估算是非常重要的。我们不可能把每个想到的算法都去实现一遍看看是否足够快。应当通过估算算法的复杂度来判断所想的算法是否足够应付题目给出的数据规模。复杂度分为时间复杂度和空间复杂度。通俗一点说,就是『跑多快』和『占多大内存』。在分析复杂度时,我们通常考虑它与什么成正比,并称之为算法的阶。比如程序执行了一个四重循环,每重循环执行n次,那么运行时间就和n4成正比。我们将与n4成正比写作O(n4)。再比如程序开辟了一个二维数组,下标范围是[0..n-1][0..n-1],那么程序的空间复杂度就是O(n2)。程序的运行时间不仅取决于时间复杂度,也会受计算复杂度、递归等因素的影响,但因此造成的差距最多也就是几倍。估算一个复杂度是否能承受目前的数据规模时,就将数值可能的最大值待遇复杂度的渐进式中,就能简单的判断算法是否能够满足运行时间限制的要求。比如O(n2)的算法,将n=1000代入得到1000000。通常来讲按照上述方法计算得到108基本是1s时限的最大值。当数组长度为N时,二分查找的时间复杂度为O(lgN),把时间复杂度中的一个N优化到lgN是很常用的策略。同一道题的不同解法,即使时间复杂度相同也有可能运行时间差出很多倍,因此对于代码细节的效率优化也同样重要,尤其是重复计算的化简。

分治,即“分而治之”,就是把一个复杂的问题分成两个或更多的相同或相似的子问题,直到最后子问题可以简单的直接求解,原问题的解即子问题的解的合并。总结起来就是:分解、解决、合并,这三大步。分治策略最经典的一个例子就是折半搜索算法,也就是二分查找。二分查找是在有序数组中查找某一特定元素的搜索算法。搜索过程中从数组的中间元素开始,判断待查元素和中间元素的关系,来判断接下来是选择哪一半继续搜索。由于折半搜索算法每次都把搜索区域减少一半,所以时间复杂度是O(logN),要比朴素算法的O(N)高效许多,这也就是分治策略的优势所在:高效。分治策略的其他经典例子还有排序算法中的归并排序。归并排序的思路是将待排序的元素分成大致相同的两个子集合,分别对两个子集合进行排序,最终将排序的子集合合并成排好序的集合。分治所能解决的问题一般具有以下特征:问题的规模缩小到一定程度就可以轻松解决;问题可以分解为若干个规模较小的相同子问题;分解出的多个子问题的解可以合并为原问题的解。另外如果每个子问题之间不是独立的,那么就要重复地求解公共的子问题,此时更适合用我们后面会讲到的动态规划算法。

 

Codeforces(codeforces.com)是俄罗斯创办的OJ,会定期举办算法比赛,比赛分为div1和div2,有一套积分和颜色系统,红色最高,红名用户拥有很多特权。

posted on 2015-12-22 22:26  泽口靖子  阅读(570)  评论(0编辑  收藏  举报

导航