1、1国际大学生程序设计竞赛辅导教程郭嵩山 崔昊 吴汉荣 陈明睿 著北京大学出版社i前言ACM 国际大学生程序设计竞赛(ACM International Collegiate Programming Contest,简称 ACM/ICPC)是由国际计算机界历史最悠久、颇具权威性的组织 ACM 学会(Association for Computer Machinery)主办,是世界上公认的规模最大、水平最高的国际大学生程序设计竞赛,其目的旨在使大学生运用计算机来充分展示自已分析问题和解决问题的能力。该项竞赛从 1970 年举办至今已历 25 届,因历届竞赛都荟萃了世界各大洲的精英,云集了计算机界的
2、“希望之星” ,而受到国际各知名大学的重视,并受到全世界各著名计算机公司的高度关注,成为世界各国大学生最具影响力的国际级计算机类的赛事,ACM所颁发的获奖证书也为世界各著名计算机公司、各知名大学所认可。该项竞赛分区域预赛和世界决赛两个阶段进行,各预赛区第一名自动获得参加世界决赛的资格,世界决赛安排在每年的 34 月举行,而区域预赛安排在上一年的 9 月12 月在各大洲举行。ACM/ICPC的区域预赛是规模很大,范围很广的赛事,近几年,全世界有 1000 多所大学,近 2000 支参赛队在六大洲的 2830 个赛站中争夺世界决赛的 60 个名额,其激烈程度可想而知。与其他编程竞赛相比,ACM/I
3、CPC 题目难度更大,更强调算法的高效性,不仅要解决一个指定的命题,而且必需要以最佳的方式解决指定的命题;它涉及知识面广,与大学计算机系本科以及研究生如程序设计、离散数学、数据结构、人工智能、算法分析与设计等相关课程直接关联,对数学要求更高,由于采用英文命题,对英语要求较高,ACM/ICPC 采用 3 人合作、共用一台电脑,所以它更强调团队协作精神;由于许多题目并无现成的算法,需要具备创新的精神,ACM/ICPC 不仅强调学科的基础,更强调全面素质和能力的培养;由于 ACM/ICPC 是采用 5 小时全封闭式竞赛,参赛队员与外界完全隔离,完全独立完成,没有任何水份,是其实际能力的真实表露,其成
4、绩可信度甚高;但ACM/ICPC 又是一种“开卷考试” ,可以带任何书籍、资料甚至源程序代码清单(但不能带软盘) ,不需要去死背算法,而强调的是算法的灵活运用;与其它计算机竞赛(如软件设计,网站设计等)相比,ACM/ICPC 有严谨而客观的评判规则(严格的数据测试) ,排除了因评委的主观因素而造成评审不公平的现象,所以,ACM/ICPC 对成绩的争议较少,大家比较心服口服。 综观近三年(19982000)中山大学共参加了 6 次地区预赛,成绩全部在三甲之列:连续三年夺得上海站季军(19992000 年还连续两年夺得上海站第四名) 、夺得台北站冠军、ii香港站亚军和日本站亚军;并连续三年争得了世
5、界决赛权。并在第 24 届(2000 年)在美国佛罗里达州奥兰多市举行世界决赛中夺得了第 11 名的好成绩,在第 25 届(2001)在加拿大温哥华市举行的世界决赛中首获铜牌(世界第 14 名) 。为了帮助高等院校的大学生们备战国际大学生程序设计竞赛,帮助他们提高程序设计水平和培养更强的分析问题和解决问题的能力,我们编写了这本辅导教材。本书所用的语言是 Pascal(Delphi) 。全书共分六章,第 1 章先介绍 Delphi 的运行环境,以便于读者能更好地读通后面各章的程序;第 2 章采用精讲的方式,简明扼要、深入浅出地介绍了在国际大学生程序设计竞赛中经常用到的各种典型算法;而在第 3 章
6、中,我们着重介绍了寻找最优解的算法,诸如图论中的搜索算法和如何运用动态规划的思维来解决实际问题等方法;在第 4 章中,我们从 ACM/ICPC 世界决赛和区域预赛试题中精选了有代表性的 10 道例题,通过对例题的详细分析,力图让读者能更深刻地理解第 2、3 章中所介绍的基本算法;在第5 章中,我们精选了一批有代表性的试题作为习题,并为这些习题设计了严格的、有梯度的测试数据,以便于读者检验自己编程的正确性;而在第 6 章中,我们给出了这些习题的详细分析和解答。为便于读者们学习和理解,本书的全部例题和习题都给出了我们自己编写的参考程序,而所有参考程序都有详细的注释。参加编写本书的 4 位作者,第一
7、位是国际大学生程序设计竞赛中山大学队的教练,其余 3 位都是参加过多次世界决赛和亚洲多个赛站区域预赛的中山大学队的主力队员。他们都是在校的研究生,我们期望能将自己的知识、经验、心得和体会,奉献给广大的程序设计爱好者,以便与大家共同探讨和交流。本书可以作为高等院校大学生和研究生们准备参加各级国际大学生程序设计竞赛活动的辅导教材和试题集,也可以作为高等院校研究生和本科高年级学生学习相关课程的参考书,也可以作为省级及以上信息学奥林匹克优秀选手准备高层次程序设计竞赛的参考用书。中山大学计算机科学系 97 级孔颖同学(也是参加过两次世界决赛和亚洲多个赛站区域预赛的中山大学队的主力队员)曾参与过本书的一些
8、程序的编写工作,在此表示衷心的感谢。由于我们水平所限,书中难免有不足之处,欢迎读者批评指正,谢谢!作者iii2001 年 10 月国际大学生程序设计竞赛辅导教程第 1 页目录前言1第一章 Delphi 简介 .1第一节 Delphi 的运行环境 1第二节 Delphi 常量 5第三节 Delphi 变量 5第四节 Delphi 类型 6第五节 Delphi 的基本语句 11第六节 Delphi 函数与过程 15第七节 函数的递归 17第八节 面向对象 Object Pascal.18第九节 Delphi 中使用嵌入汇编 19第二章 基本算法介绍 21第一节 概述 21第二节 常用数据结构在 D
9、elphi 中的实现 .21第三节 枚举算法 32第四节 回溯算法 33第五节 贪心算法 35第六节 分治算法 38第七节 数值计算 39第八节 计算几何 47第九节 模拟题解法 51第三章 寻找最优解的算法 53第一节 动态规划 53第二节 最短路问题 61第三节 搜索算法 70第四章 国际大学生程序设计竞赛(ACM/ICPC)试题及分析 89第一节 生成字符串 89第二节 模式识别的“中心”问题 94第三节 划分凸多边形 98第四节 防卫导弹 100第五节 邮票问题 104第六节 骨牌矩阵 107第七节 师生树 113第八节 旅游预算 119第九节 正整数竖式除法 127第十节 移棋子 1
10、31第五章 习题 141第一节 习题 141第二节 部分习题测试数据及参考答案 151第六章 习题解答 173国际大学生程序设计竞赛辅导教程第 2 页第一节 电子表格 ( Table )173第二节 DEL 命令 (DEL) 176第三节 分割方格 (Divide)181第四节 信息编码 (Decode).189第五节 海上交通控制 ( Lane ).191第六节 投递最佳路线(Best Deliver) .199第七节 计算机网络连接(computer network) 206第八节 联系圈(Circle) .210第九节 球钟(Ball Clock) 213第十节 建筑物(Building
11、s) .216附录: 1997-2000 年(第 2225 届)ACM 国际大学生程序设计竞赛(ACM/ICPC)亚洲区预赛成绩 226参考资料 230国际大学生程序设计竞赛辅导教程第 1 页第一章 Delphi 简介第一节 Delphi 的运行环境1.1.1 Delphi 简介Delphi 是著名的 Borland 公司开发的可视化软件开发工具。 “真正的程序员用 c,聪明的程序员用 Delphi”,这句话是对 Delphi 最经典、最实在的描述。 Delphi 被称为第四代编程语言,它具有简单、高效、功能强大的特点。和 VC 相比,Delphi 更简单、更易于掌握,而在功能上却丝毫不逊色;
12、和 VB 相比,Delphi 则功能更强大、更实用。可以说 Delphi 同时兼备了 VC 强大的功能和 VB 简单易学的特点。因此,它一直是程序员至爱的编程工具。Delphi 具有以下的特性:基于窗体和面向对象的方法,高速的编译器,强大的数据库支持,与 Windows 编程紧密结合,强大而成熟的组件技术。但最重要的还是 Object Pascal语言,它才是一切的根本。Object Pascal 语言是在 Pascal 语言的基础上发展起来的,简单易学。Delphi 提供了各种开发工具,包括集成环境、图像编辑(Image Editor) ,以及各种开发数据库的应用程序,如 Desktop D
13、ataBase Expert 等。除此之外,还允许用户挂接其它的应用程序开发工具,如 Borland 公司的资源编辑器(Resourse Workshop ) 。在 Delphi 众多的优势当中,它在数据库方面的特长显得尤为突出:适应于多种数据库结构,从客户机服务机模式到多层数据结构模式;包括高效率的数据库管理系统和新一代更先进的数据库引擎;具备最新的数据分析手段和提供大量的企业组件。Delphi 发展至今,从 Delphi、Delphi 到现在的 Delphi6,不断添加和改进各种特性,功能越来越强大。本书将以 Delphi6 为基础,介绍 Delphi 的开发环境、基本概念,让读者能在 D
14、elphi 环境下编写竞赛程序。1.1.2 Delphi 的 IDE 环境启动 Delphi 后,屏幕上会出现如图 1.1 所示的界面,这是使用 Delphi 开发 Windows 应用程序和 Internet 应用程序常见的界面。国际大学生程序设计竞赛辅导教程第 2 页图 1.1与上面两种应用不用,编写竞赛程序常常是使用控制台应用程序(Console Application) ,创建一个新的控制台应用程序可以使用如下的步骤:(1)使用菜单 File - New - Other,如图 1. 2 所示图 1. 2(2)在 New Items 对话框中选择新建 “Console Applicati
15、on”( 图 1. 3)国际大学生程序设计竞赛辅导教程第 3 页图 1. 3新建的控制台应用程序界面如图 1. 4 所示:图 1. 4下面将开始我们的第一个程序:Hello 程序,并且在 Delphi 环境中运行这个程序。程序在代码编辑窗口中编辑,例如可以在代码编辑窗口中输入 Hello 程序:program Hello;$APPTYPE CONSOLE代码编辑窗口菜单和工具栏国际大学生程序设计竞赛辅导教程第 4 页usesSysUtils;begin TODO -oUser -cConsole Main : Insert code here WriteLn(Hello World.);Rea
16、dLn;end.其中,只有下划线部分是手工输入的,其他部分是 Delphi 产生的代码。语句“WriteLn(Hello World.)”是输出“Hello World.”的字样;语句“ReadLn”是等待输入一个回车符号,以便观察程序输出结果。然后选择菜单”Run-Run”运行程序。这时,屏幕中会出现“Hello World.”的字样,如图 1. 5 所示。图 1. 51.1.3 Delphi 程序的编译、运行和调试下表列出了编译、运行和调试 Delphi 程序的常用命令:功能 菜单命令 快捷方式编译(Compile) Project - Complite Ctrl + F9运行(Run)
17、Run - Run F9单步跟踪(Trace Into) Run - Trace Into F7单步执行(Step Over) Run - Step Over F8执行到光标所在位置(Run to Cursor)Run - Run to Cursor F4设置(Breakpoint) Run - Add Breakpoint 鼠标单击代码行左边的蓝点观察窗口(Watch) Run - Add Watch Ctrl + F5国际大学生程序设计竞赛辅导教程第 5 页更多的命令请参考 Delphi 的联机帮助。第二节 Delphi 常量标识符是 Delphi 应用程序中一些量的名称,这些量包括变量(
18、var)、常量(const)、类型(type)、过程(procedure)、方法(Method)及其他,Object Pascal 在应用标识符时,必须首先说明它们。Object Pascal 是强类型语言,它的编译器可以检查确保赋给变量或属性的值是正确的类型,以便于您改正错误。由于 Object Pascal 是编译语言,所以 Delphi 的执行速度要比使用解释语言快得多。在使用标识符前说明它们,可以减少程序错误并增加代码的效率。有一点需要特别说明的是 Object Pascal 中的标识符是不区分大小写的,这和 C/C+语言有很大的区别。例如:Delphi 和 delphi 被看成是同一
19、个标识符。常量说明是为一个标识符赋予一个值,在程序执行过程中是不可改变的。常量说明使用保留字“const”作为开头。格式为:const 常量名=常量值;下面的例子声明了三个常量:constPi = 3.14159;Age = 34;ProductName = Delphi;上文的三个常量分别是实型、整型和字符串型常量。常量用“= “ 表示两边的值是相等的。和 C/C+语言不同,Object Pascal 的字符串常量是使用单引号( )作为定界符的,例如上面的Delphi,而不是使用双引号(”) 。第三节 Delphi 变量变量是程序代码中代表一个内存地址的标识符,而该地址的内存内容在程序代码执
20、行时可以被改变。在使用变量前必须对它进行说明,即对它进行命名,并说明它的类型。在所有变量说明以前加上保留字 var。变量说明左边是变量的名称,右边则是该变量的类型,中间用(:)隔开。即格式为:var 变量名 :变量类型 ;例如:varValue ,Sum : Integer;Name : String;在 Delphi 中,可以在变量声明同时赋初始值,格式为:const 变量名:变量类型=变量初始值 ;注意,此时是使用 const 保留字开头,而不是 var。例如:const国际大学生程序设计竞赛辅导教程第 6 页Value: Integer:=10;Name : String=Gates;第
21、四节 Delphi 类型Delphi 有两大数据类型,一类是系统已经预定义的,另一类是用户自定义的。 Object Pascal 有一些系统预定义的数据类型,这些类型包括有:整型、实型、布尔型、字符型、指针型;而用户自定义的数据类型有枚举型、子界型、数组型、集合型、记录型、对象型等,您可以利用这些用户预定义的数据类型来构造新的数据类型以满足程序的特定需要。1.4.1 Delphi 预定义类型Object Pascal 有多个预定义的数据类型,您可以说明任何这些类型的变量:1整型:与 CPU 和操作系统相关的整型包括 Integer 和 Cardinal,在当前 32 位编译器下,取值范围如下:
22、类型 范围 格式Integer -21474836482147483647 signed 32-bitCardinal 04294967295 unsigned 32-bit与 CPU 和操作系统无关的整型如下:类型 范围 格式Shortint -128127 signed 8-bitSmallint -3276832767 signed 16-bitLongint -21474836482147483647 signed 32-bitInt64 -263263-1 signed 64-bitByte 0255 unsigned 8-bitWord 065535 unsigned 16-bit
23、Longword 04294967295 unsigned 32-bit2实型:下表列出了实型的范围和存储格式:类型 范围 有效位 占用字节(bytes)Real48 2.9 x 10-391.7 x 1038 11-12 6Single 1.5 x 10-453.4 x 1038 7-8 4Double 5.0 x 10-3241.7 x 10308 15-16 8Extended 3.6 x 10-49511.1 x 104932 19-20 10Comp -263+1 263-1 19-20 8Currency -222337203685477.5808922337203685477.5
24、807 19-20 8通用类型 Rea 在当前的解释下,等价于 Double。类型 范围 有效位 占用字节(bytes)Real 5.0 x 10-3241.7 x 10308 15-16 8国际大学生程序设计竞赛辅导教程第 7 页3布尔型:Boolean,只包含 true 或 False 两个值,占用 1 字节内存。4字符型:Char,一个 ASCII 字符;字符型的常量形式上和字符串型常量一样,都是使用单引号()作为定界符,例如:A。5字符串类型:String 一串最长可达 2G 个 ASCII 字符。6指针型:Pointer,可以指向任何特定类型。相当与 C/C+中的“ void*”。类
25、型的兼容性:整型类别和实型类别都各有五种类型,同一类别中,所有的类型与其他同类别的都相容,您可以将一种类型的值赋给相同类别中不同类型的变量或属性,而只需要这个值的范围在被赋值的变量或属性的可能值范围内。例如,对于一个 Shortint 型的变量,可以接受在-128 到 127 范围内的任意整数,例如,对于 Shortint 类型,您不能将 128 赋给它,因为128 已经超出了 Shortint 的范围了;此时,如将范围检查功能打开( 选用 Options|Project,并在 Compiler Options Page 中选择 Range Checking),将会检查出一个范围错误;如果 R
26、ange Checking 没有被打开,那么程序代码将可以执行,但被赋值的值将不是您期望的值。在一些情况下,您可以进行不同类型的变量或属性的赋值。一般来说,可以将一个较小范围的值赋给一个较大范围的值。例如,您可以将整型值 10 赋给一个能接受实型值的Double 类型的变量,而使其值成为 10.0,但如果将一个 Double 类型的值赋给整型变量,则会出现类型错误。如果您不清楚类型的兼容性,可以参阅 Delphi 的在线帮助中“Type Compatibility and Assignment Compatibility”主题。1.4.2 枚举类型一个枚举型的声明列出了所有这种类型可以包括的值
27、,枚举型的声明格式如下:type typeName = (val1, ., valn);其中,typeName,val1,.,valn 都是合法的标识符。例如:typeTSound = (tsClick, tsClack, tsClock);TMyColor = (mcRed, mcBlue, mcGreen, mcYellow, mcOrange);可以定义上述枚举类型的变量和对该变量赋值如下:var MyColor:TMyColor; MyColor:=mcBlue;在枚举型中,括号中的每一个值都有一个由说明它的位置决定的整型值。例如 mcRed国际大学生程序设计竞赛辅导教程第 8 页有整
28、型值 0,mcBlue 有整型值 1 等。您可以把 MyColor 说明为一个整型变量,并将每种颜色赋一个整型值以达到相同的效果,但用枚举型会使得程序可读性好,编写容易。当您在枚举型中列出值时,您同时说明了这个值是一个标识符。例如您的程序中如果已经含有TMyColor 类型且说明了 MyColor 变量,则程序中便不能使用 mcRed 变量,因为它已经被说明为标识符了。1.4.3 子界类型子界型是下列这些类型中某范围内的值:整型、布尔型、字符型或枚举型。任何形如LowHigh 的构造都表示一个子界类型,其取值范围是从 Low 到 High。例如:type TColors = (Red, Blu
29、e, Green, Yellow, Orange, Purple, White, Black);/(枚举类型)type TMyColors = GreenWhite;typeSomeNumbers = -128127;Caps = AZ;子界型限定了变量的可能取值范围。当范围检查打开时,(在库单元的 Implementation后面有$R*.DFM 字样表示范围检查打开,否则您可以在 Options|Project|Complier Options中选择 Range Cheking 来打开范围检查 ),如果变量取到子界以外的值,会出现一个范围检查错误。1.4.4 数组类型数组是某种数据类型的有
30、序组合,其中每一个元素的值由其相对位置来指定,您可以在数组的某个位置上放置数据,并在需要时使用这些数据。数组的声明形式为:array indexType1, ., indexTypen of baseType其中,indexType 是基本类型,它的范围应该在 2G 以内,baseType 是数组的基类型。一般来说,indexType 是子界类型。请看下面的例子:/声明数组var MyArray: array1100 of Char; /Example 1type TMatrix = array110 of array150 of Real; /Example 2type TMatrix =
31、array110, 150 of Real; /Example 3var MyMatrix:TMatrix; /Example 4/Usage of Arrays 使用数组元素的范例MyArray1:=A;MyMatrix150:=0.0;MyMatrix10,50:=1.0;WriteLn(MyArray10);Example 1 中,声明了一维数组 MyArray,包含 100 个 Char;Example2 和 Example3都可以定义了二维数组的类型,这两种写法都是等价的。Example4 声明上述二维数组类型的变量。您也可以在声明数组时,给数组赋初始值,例如:国际大学生程序设计竞赛
32、辅导教程第 9 页/给数组赋初始值const MyArray:array 13,13 of Integer=(1,2,3),(4,5,6),(7,8,9);const MyChars:array 12 of Char=(A,B);1.4.5 字符串类型字符串类型本质上是一个一维的字符数组。当您说明一个字符串型的变量时,您可以指明这个字符串的大小,下面是说明字符串类型的例子:type MyString: string15;var MyName: MyString;var tmpString:string;则变量 MyName 被说明成为最多可以包含 15 个字符。如果您没有说明字符串的大小,De
33、lphi 会认为字符串最多包含最大值 2G 个字符(如变量 tmpString) 。给字符串赋值可以直接使用单引号括起的字串赋值:MyName := Bill Gates;因为 MyName 是一个可以包含 15 个字符的 MyString 型变量。当您给字符串型变量赋的值多于定义数值时,例如将 MyName 赋为FrankSmith.Franklin ,则 Delphi 只会接受前15 个字符FrankSmith.Fran 。您可以使用索引值来访问字符串的字符,例如,用MyName1可以得到 MyName 的第一个字符B ,也可以直接地修改字符串中的字符,例如:MyName1=F。您可以充分
34、利用 Delphi 丰富的运算符、过程和函数来处理字符串型的变量和属性。详细的请参阅“Delphi 函数与过程”中关于常用的字符串函数部分的内容。1.4.6 集合类型集合类型是一群相同类型元素的组合,这些类型必须是有限类型如整型、布尔型、字符型、枚举型和子界型,并且该类型的元素个数不得多于 256 个。在检查一个值是否属于一个特定集合时,集合类型非常有用。声明集合使用“set of”来声明,请看例子:type /定义集合类型TSomeInts = 1250;TIntSet = set of TSomeInts;type TIntSet2 = set of 1250; /和上面的方式等价var
35、Set1, Set2: TIntSet; /声明集合变量Set1 := 1, 3, 5, 7, 9, 100200; /给集合赋值Set2 := 2, 4, 6, 8, 10;集合使用方括号为定界符,里面的元素使用逗号作为分隔。集合的操作也比较丰富,常用的操作见下表:操作符 操作说明 参数类型 结果类型 例子+ 合集 集合,集合 集合 Set1 + Set2- 差集 集合,集合 集合 S - T国际大学生程序设计竞赛辅导教程第 10 页* 交集 集合,集合 集合 S * T= 超集 集合,集合 布尔 S1 = S2= 等于 集合,集合 布尔 S2 = MySetS1in 成员 基类型,集合 布
36、尔 A in Set11.4.7 记录类型记录是您的程序可以成组访问的一组数据的集合。下面的例程说明了一个记录类型的用法:typeTEmployee=recordName : string20;YearHired:19902000;Salsry: Double;Position: string20;end;记录包含可以保存数据的域,每一个域有一个数据类型。上文的记录 TEmployee 类型就含有四个域。您可以用以下的方式说明记录型的变量:var NewEmployee,PromotedEmployee:TEmployee;用如下的方法可以访问记录的单域:NewEmployee.Salary
37、:= 1000;编写如下的语句可以给整个记录赋值:with PromotedEmployee dobeginName :=;YearHired := 1993;Salary := 2000.00Position := editor;end;您的程序可以将记录当成单一实体来操作:PromptEmployee := NewEmployee;以上介绍了用户常用的自定义类型。在 Delphi 的编程中,对象是非常重要的用户自定国际大学生程序设计竞赛辅导教程第 11 页义数据类型。象记录一样,对象是结构化的数据类型,它包含数据的域(Field),也包含作为方法的过程和函数。关于对象的详细内容请参考“面向
38、对象 Object Pascal”一节。1.4.8 指针类型您可以通过下面的语法声明任何类型的指针:type pointerTypeName = BaseType当您定义了一个记录或者是其他数据类型时,通常都会定义一个指向这种类型的指针,这样使用这种数据类型的实例时,可以避免大量重复的内存数据拷贝。下面是定义指针的例子:typeTEmployee=recordName : string20;YearHired:19902000;Salsry: Double;Position: string20;end;type PEmployee=TEmployee;创建和释放指针,分别使用 New 和 Di
39、spose 过程,下面是使用指针的例子:var aEmployee:PEmployee;beginNew(aEmployee); /创建指针aEmployee.Name:=Gates; /使用指针Dispose(aEmployee); /释放指针end.注意:在使用指针时, “”的位置与 C/C+语言不同。第五节 Delphi 的基本语句1.5.1 赋值语句赋值语句用作把一个表达式的值赋给一个变量,例如,计算圆面积:S:=Pi*R*R;在 Delphi 中,赋值号为“:=” 。赋值时,要求表达式值的类型和变量的类型兼容,关于兼容性的问题,请参考“Delphi 类型”一节。国际大学生程序设计竞赛
40、辅导教程第 12 页1.5.2 复合语句在 Delphi 中,一个语句是以分号“;”作为语句结束符的。复合语句的作用是把若干个语句组合起来,在逻辑上看成是一个语句。复合语句的格式是使用“begin”和“end”把一组语句组合起来,例如:if J 0 rhentest1(A); /在 Test2 中调用已执行的过程 Test1 end;/主程序beginAlpha := 15; /给 Alpha 赋初值Test1(Alpha); / 第一次调用 Test1,递归开始end.Alpha 赋初值后,调用 Test1,实现先减 1 再除 2 的循环递归调用,直到 Alpha 小于0 为止。第八节 面向
41、对象 Object PascalDelphi 是基于面向对象编程的先进开发环境。面向对象的程序设计(OOP)是结构化语国际大学生程序设计竞赛辅导教程第 19 页言的自然延伸。OOP 的先进编程方法,会产生一个清晰而又容易扩展及维护的程序。一旦您为您的程序建立了一个对象,您和其他的程序员可以在其他的程序中使用这个对象,完全不必重新编制繁复的代码。对象的重复使用可以大大地节省开发时间,切实地提高工作效率。但是就竞赛而言,竞赛是富于创造性的活动,代码的重用率不高,因此对象在实际中使用的频率不高。下面主要讨论使用 Delphi 中的对象,至于 Delphi 如何实现对象机制,请参考 Delphi的联机
42、帮助或者相关参考书。使用对象前,需要对对象实例化,一般是通过调用 Create 方法来完成,释放对象的实例,一般通过调用 Free 方法来完成。使用对象中的属性和方法,通过 “对象名.属性”和“对象名.方法” ,例如:(TStringList 是字符串列表类)程序 ex1_8.dprprogram ex1_8;$APPTYPE CONSOLEusesSysUtils,Classes;varT:TStringList; /声明对象beginT := TStringList.Create; /对象实例化T.Add(Delphi 6.0); /调用对象的方法WriteLn(T.Count); /引用
43、对象的属性T.Free; /释放对象ReadLn;end.第九节 Delphi 中使用嵌入汇编在 Delphi 中可以直接嵌入汇编,使用汇编的好处是可以提高程序运行的效率,但是,另一方面,使用汇编会加大编程的难度和调试的难度。因此在竞赛中往往只要一些关键而又简单的小程序段才使用汇编来编写。Delphi 目前是使用 32 位的汇编,请先看下面的例子:program ex1_9;$APPTYPE CONSOLEusesSysUtils;function Counter(var A;Count:Integer):Integer;label lbegin,lloop;beginasmMov ESI,M
44、ov ECX,Mov EAX,0;Mov BL,0;lbegin: CMP ESI,BL;JNE lloop;Inc EAX;国际大学生程序设计竞赛辅导教程第 20 页lloop: Inc ESI;loop lbegin;Mov end;end; var X:array 110 of Byte;beginFillchar(X,sizeof(X),0);X5:=1;WriteLn(Counter(X,10);ReadLn;end.函数 Counter 的功能是统计长度为 Count 的 Byte 类型数组 A 中“0”的个数。使用汇编应该注意:(1)嵌入汇编是以”asm”开头,并以”end”结尾
45、的部分,进入和退出汇编是不需要保存和回复各个寄存器的值,因为这项工作 Delphi 会自动完成。(2)汇编中使用的标号,被认为是 Delphi 的标号,需要使用 label 来声明。(3)在汇编中,对 Delphi 原有变量的引用,需要使用“element:array 0max of Pointer;end;其中,max 是最多容纳元素的个数。实现上述的五种操作的程序段如下:unit LinearList;/求线性表的长度function LLength(var alist:TLinearList):Integer;beginResult:=alist.len;end;/求线性表中第 n 个位
46、置的元素。function LGet(var alist:TLinearList;n:Integer):Pointer;beginif (n=alist.len) then Result:=nilelse Result:=alist.elementn;end;/位置 n 前插入元素procedure LInsert(var alist:TLinearList;n:Integer;Value:Pointer);beginif n=alist.len thenalist.elementalist.len := ValueelsebeginMove(alist.elementn,alist.elem
47、entn+1,SizeOf(Pointer)*(alist.len-n);alist.elementn := Valueend;Inc(alist.len);end;/删除位置为 n 的元素procedure LDelete(var alist:TLinearList;n:Integer);beginif (n=0) and (nalist.len) thenbeginMove(alist.elementn+1,alist.elementn,SizeOf(Pointer)*(alist.len-n-1);Dec(alist.len);end;end;/查找元素function LFind(va
48、r alist:TLinearList;Value:Pointer):Integer;var I:Integer;beginfor I:=0 to alist.len -1 doif alist.elementI=Value thenbeginResult:=I;exit;end;Result:=-1;国际大学生程序设计竞赛辅导教程第 23 页end;说明, (1)Pointer 是 Delphi 中比较通用的类型,可与其他类型转换。 (2)在实现中,用到了 Move 函数,在运行速度方面要比使用循环逐个元素移动快得多。 (3)程序中没有对元素个数多于 EMAX 进行判断,这是因为在实际的竞赛
49、中,我们总是开出一个足够大的数组,不需要考虑溢出问题。下面是使用上面函数的例子,主要用意是演示如何在 Pointer 之间 Integer 的转换。程序 ex2_2_1.dprprogram ex2_2_1;$APPTYPE CONSOLEusesSysUtils,LinearList in LinearList.pas;var a:TLinearList;beginLEmpty(a);LInsert(a,0,Pointer(2);LInsert(a,0,Pointer(1);WriteLn(Integer(LGet(a,0), ,Integer(LGet(a,1);LDelete(a,0);WriteLn(Integer(LGet(a,0), ,Integer(LGet(a,1);