代码点评及解析:陶陶摘苹果
目的:利用这篇文章,向程序设计的初学者,介绍一下“如何利用计算机解决实际问题”这个听起来好大好大的问题。
先来讲评一下“陶陶摘苹果”这个问题。该题源于:2005年NOIP复赛普及组第一题。具体如下:
【问题描述】
陶陶家的院子里有一棵苹果树,每到秋天树上就会结出10个苹果。苹果成熟的时候,陶陶就会跑去摘苹果。陶陶有个30厘米高的板凳,当她不能直接用手摘到苹果的时候,就会踩到板凳上再试试。
现在已知10个苹果到地面的高度,以及陶陶把手伸直的时候能够达到的最大高度,请帮陶陶算一下她能够摘到的苹果的数目。假设她碰到苹果,苹果就会掉下来。
【输入文件】
输入文件apple.in包括两行数据。第一行包含10个100到200之间(包括100和200)的整数(以厘米为单位)分别表示10个苹果到地面的高度,两个相邻的整数之间用一个空格隔开。第二行只包括一个100到120之间(包含100和120)的整数(以厘米为单位),表示陶陶把手伸直的时候能够达到的最大高度。
【输出文件】
输出文件apple.out包括一行,这一行只包含一个整数,表示陶陶能够摘到的苹果的数目。
【样例输入】
100 200 150 140 129 134 167 198 200 111
110
【样例输出】
5
这个问题,不是很困难的。但是,对于还没有学习过“数组”概念的初学者来说,恐怕就只能用“输入输出重定向”的方法来读取并且解决问题了。我先来谈谈这个方法:
【解题思路】
利用输入输出重定向。
不使用数组存储技术,解决本题。
1、读入陶陶把手伸直的时候能够达到的最大高度(taoHeight)。
2、重新重定向输入文件
3、读入每个苹果的高度,并计数(counts)能摘得到的苹果。
我们注意到,在这种解法中,需要2次利用输入重定向功能,目的是为了分别读入“陶陶把手伸直的时候能够达到的最大高度(taoHeight)”和“每个苹果的高度”。
源代码如下:apple.c
0001 # include "stdio.h" 0002 0003 # define APPLES_AMOUNT (10) /*苹果的个数*/ 0004 # define HEIGHT_OF_CHAIR (30) /*椅子高30厘米*/ 0005 0006 int main() { 0007 int taoHeight, /*陶陶把手伸直的时候能够达到的最大高度(厘米)*/ 0008 appleHeight, /*苹果的高度*/ 0009 counts, /*陶陶能摘到的苹果*/ 0010 i; 0011 /*读入陶陶把手伸直的时候能够达到的最大高度(taoHeight)*/ 0012 freopen("apple.in","r",stdin); 0013 /*跳过输入文件开头的APPLES_AMOUNT个数据(苹果高度)*/ 0014 for (i=1; i<=APPLES_AMOUNT; i=i+1) scanf("%d",&appleHeight); 0015 /*读入陶陶把手伸直的时候能够达到的最大高度(厘米)*/ 0016 scanf("%d",&taoHeight); 0017 0018 /*重新重定向输入文件*/ 0019 freopen("apple.in","r",stdin); 0020 0021 /*读入苹果的高度,并计数(counts)能摘得到的苹果。*/ 0022 counts=0; 0023 for (i=1; i<=APPLES_AMOUNT; i=i+1) { 0024 scanf("%d",&appleHeight); 0025 if (appleHeight<=(taoHeight+HEIGHT_OF_CHAIR)) counts=counts+1; 0026 } 0027 0028 /*重定向输出文件*/ 0029 freopen("apple.out","w",stdout); 0030 printf("%d",counts); 0031 0032 return 0; 0033 }
另一方面,对于学过了“数组”概念的学生,自然可以考虑利用数组来解决本题了:
【另一种解题思路】:利用数组存储。
利用数组存储苹果的高度。
1、把苹果的高度读入数组apples(存储每个苹果的高度)
2、读入陶陶把手伸直的时候能够达到的最大高度(taoHeight)
3、扫描数组apples,计数(counts)能摘得到的苹果。
在这里,我们只需要读入1次输入文件(apple.in)即可。源代码如下:
0001 # include "stdio.h" 0002 0003 # define APPLES_AMOUNT (10) /*苹果的个数*/ 0004 # define HEIGHT_OF_CHAIR (30) /*椅子高30厘米*/ 0005 0006 int main() { 0007 int taoHeight, /*陶陶把手伸直的时候能够达到的最大高度(厘米)*/ 0008 apples[APPLES_AMOUNT], /*苹果的高度*/ 0009 counts, /*陶陶能摘到的苹果*/ 0010 i; 0011 /*读入陶陶把手伸直的时候能够达到的最大高度(taoHeight)*/ 0012 freopen("apple.in","r",stdin); 0013 /*把苹果的高度读入数组apples(存储每个苹果的高度)*/ 0014 for (i=0; i<=APPLES_AMOUNT-1; i=i+1) scanf("%d",&apples[i]); 0015 /*读入陶陶把手伸直的时候能够达到的最大高度(厘米)*/ 0016 scanf("%d",&taoHeight); 0017 0018 /*描数组apples,计数(counts)能摘得到的苹果。*/ 0019 counts=0; 0020 for (i=0; i<=APPLES_AMOUNT-1; i=i+1) { 0021 if (apples[i]<=(taoHeight+HEIGHT_OF_CHAIR)) counts=counts+1; 0022 } 0023 0024 /*重定向输出文件*/ 0025 freopen("apple.out","w",stdout); 0026 printf("%d",counts); 0027 0028 return 0; 0029 }
上述代码,都可以达到解题的目的。
解题的源代码及测试的数据文件,可以从这里下载到:http://cid-bfe8af46e42e3ecf.office.live.com/self.aspx/%e7%ae%97%e6%b3%95%e7%ab%9e%e8%b5%9b/%e6%ba%90%e4%bb%a3%e7%a0%81/%e7%ae%97%e6%b3%95%e7%ab%9e%e8%b5%9b%e7%bb%83%e4%b9%a04%ef%bc%8c%e6%ba%90%e4%bb%a3%e7%a0%81.rar
下面是我想说的一点感想:
利用电脑解决实际问题,常常面临的问题是:电脑的信息表达与现实世界的信息现实,存在着巨大的差异。行话来说,“现实世界的信息现实”被称为“现实域”;而“电脑的信息表达”也被称为“问题域”。上述差异,被称为“现实域与问题域之间的差异”。
这句话的意思是:现实中,陶陶是个活蹦乱跳的小朋友,苹果是红扑扑地长在树上的水果。但这些信息,对于电脑解决问题,是没什么帮助的。所以,我们要通过所谓的“抽象”,把我们所感兴趣的信息,比如:椅子的高度、陶陶的高度、苹果的高度,在电脑中进行表达。通过抽象,我们就可以缩短“现实”与“电脑”在表述问题上的差异。比如,我们用整数来存储椅子的高度、陶陶的高度、苹果的高度,就是对“椅子”、“陶陶”和“苹果”的抽象,排除了与解题无关的多余信息。这种抽象,看似简单(因为本题确实不难),但随着问题难度的增加,会变得非常的复杂。最终,“对现实世界的抽象”演变成了计算机程序设计的一门重要技能:数据结构。
再一个问题,我想讲的是“对数据的处理”。假使我们已经“成功地对现实信息进行了抽象”,如上所述,形成了几个整数。那么该如何处理这些整数呢?由上所述,你会发现,同样是为了解决问题,我们可以采用2种不同的方法:多次输入重定向处理数据、或是利用数组存储并处理数据。也就是说,由于“数据存储形式的不同”(利用数组存储,或是不用数组存储),我们处理数据的方式也会变得不同。这种“处理数据的方式”,就是所谓的“算法”。用行话来说,“算法”是针对特定的“数据结构”进行数据处理的。
所以,N·沃斯先生(Pascal语言的发明者)才会说出那句非常有名的话:“算法+数据结构=程序”。事实上,这是一本书的名字《算法+数据结构=程序》,它是N·沃斯先生所写的一本用Pascal语言介绍算法和数据结构的一本经典巨作。其中,N·沃斯先生不但详细介绍了Pascal语言的用法,而且针对排序问题,详细讨论了各种算法的性能比较。《算法+数据结构=程序》是一本能和《C程序设计语言》相媲美的经典著作,绝对值得每一位决定深入学习程序设计的人买下来反反复复地阅读。可惜,《算法+数据结构=程序》这本书市面上根本就没有再版了。我所看到的这本书,是十几年前,在福建省图书馆里翻到的(整个书皮都是撕破了再粘上去的),科学出版社198?年翻译出版的,兄弟我仔仔细细精读了2遍,感觉受益匪浅。
至于,Pascal语言,如同C语言一样,也有了长足的发展。Delphi所用的Object Pascal,就是以Pascal为基础发展起来的。不过,这又是另一个故事了……