广东省汕头市金山中学高中信息技术奥林匹克信息学竞赛

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

集合与记录类型
§6.1 集合类型
§6.1.1 集合类型的定义
集合是同类型对象的一个汇集,它是指同类型对象汇集在一起构成的数据结构。

集合的每一个对象称为集合的元素。

集合元素必须是有序简单数据类型,集合元素的类型称为集合的基类型。

集合的一般形式为:
TYPE <类型标识符> = set of <基类型>;
基类型可以是整型、字符型、布尔型、枚举型、子界型等,但不能是实型或结构类型。

例如:
TYPE letter = set of ‘A’.. ‘Z’;
var ch1, ch2 : letter;
也可以直接写成:
var ch1, ch2 : set of ‘A’.. ‘Z’;
在Pascal中集合是用一组括在方括号中的元素来表示,元素之间用逗号分隔。

如:
[A, B , C , D] 是四个枚举量的集合
[ 1 .. 20 ] 表示1到20的所有整数的集合
[ ‘0’ ] 是单元素集
[ ] 表示空集
一个集合类型变量的取值,可以是基类型中所有元素按不同的组合而构成的子集。

例如,上面说明变量ch1的类型是letter,它可以是下列的组合:
[‘A’ .. ‘Z’] 全集
[‘A’, ‘B’, ‘Q’] 任一子集
[‘A’ .. ‘C’ , ‘X’ ..‘Z’ ]
[‘A’ ] 单元素集
[ ] 空集
空集与所有的集合类型都兼容。

§6.1.2 集合类型的运算
ch1:=[ [‘A’ .. ‘C’]];
是合法的集合赋值。

对集合除可以进行赋值运算外,还可以进行以下运算:
·交(*)运算:两集合之交 S1*S2 为一集合,所得元素由S1、S2中相同的元素组成。

如: [ 0..7 ] * [ 0..4 ] = [ 0..4 ]
·并(+)运算:两集合之并 S1+S2 为一集合,所得元素由S1、S2中所有相同的元素组成。

如: [ 0..7 ] + [ 0..4 ] = [ 0..7 ]
[ 0 , 1 ] + [ 1 , 4 , 6 ] = [ 0 , 1 , 4 , 6 ]
·差(-)运算:两集合之差 S1-S2 为一集合,所得元素由只存在于S1而不在S2的那些元素组成。

如: [ 0..7 ] - [ 0..4 ] = [ 5..7 ]
·比较运算:集合可进行“=”、“>=”、“<=”、“<>”等比较运算:
等于“=”—— S1=S2,若S1与S2中所有元素均相同,结果为true,否则为false。

如: [ 0..4 ] = [ 0..4 ] 结果为true
[ 0..7 ] = [ 0..4 ] 结果为false
不等于“<>”——S1<>S2,S1与S2中至少有一个元素不同,
如: [ 0..7 ] <> [ 0..4 ] 结果为true
[ 0..4 ] <> [ 0..4 ] 结果为false
包含“>=”——S1>=S2 表示S2是S1的子集。

被包含“<=”——S1<=S2 表示S1是S2的子集。

如: [ 0..7 ] >= [ 0..4 ] 结果为true
[ 0..7 ] <= [ 0..4 ] 结果为false
[ ] <= [ 0..4 ] 结果为true
·检查(in)运算:用来检查某一元素是否属于某一集合。

如:
1 in [ 0 .. 4 ] 结果为true
5 in [ ] 结果为false
‘A’ in [‘A’ ..‘Z’ ] 结果为true
§6.1.3 集合类型的表达式
集合表达式是由集合常数、集合变量、集合构造符和集合运算符组成。

如:
k := 5;
ch2 := [ 1, 2 , 3 , 4 ] + [ k ];
运行之后,ch2中就会有5个元素:1、2、3、4、5。

注意:
..........
...........两种表达式是等价的。

......与. [ 1, 2, 3, 4, 5 ]
...[ 1..5 ]
集合运算相当快,在程序中常用集合表达式来描述复杂的测试。

例如,条件表达式:
(ch=’T’) or (ch=’u’) or (ch=’R’) or (ch=’B’)
可用集合表达式表示为:
ch in [‘T’, ‘u’, ‘R’, ‘B’ ]
又如: if (ch>=20) and (ch<=50) then <语句>
可写成: if ch in [ 20..50 ] then <语句>
§6.1.4 注意问题
集合类型是一种使用简单,节省内存而又运算速度快的数据类型,在解决某些问题时,它能使程序编写简明清晰,节省内存而又节省运行时间。

但是使用集合时必须注意以下几点:
① Pascal规定集合的元素个数不超过256个。

当实际问题所需的元素个数大于256时,可采用布尔数组代替集合类型。

所以 var i : set of integer; 的说明是错误的,因为它的元素个数超过256个。

②集合类型变量不能进行算术运算,也不允许用读/写语句直接输入/输出集合。

所以集合的建立要通过赋值语句实现,或先初始化一个集合,然后通过并(+)运算向集合中逐步加入各个元素;集合的输出也必须间接地转换,如集合中的元素是数字或字母,可通过序数值的转换关系输出对应的字符。

③集合的元素是无序的,所以 ord , pred 和succ 函数不能用于集合类型的变量。

【例1】用集合方法编程,实现把100以内的全部素数找出来,然后把求得的每十个素数排成一行,形成素数表。

算法分析:用筛法求素数。

第一步,定义一个集合类型,如sss,它包含99个元素,从2到100;
第二步,定义两个集合变量,如筛集合 s 和素数集合 p,它们是sss类型的变量;第三步,按筛法找出全部素数;
第四步,间接输出素数表。

算法求精如下:
①把2到100逐步放入筛中,建立筛集合s;
②选定筛中最小的素数——2;
③把选定的素数放入素数集合P中;
④检查筛集合s,从中删去选定素数和它的所有倍数;
⑤重复步骤2、3、4,直到筛集合s变成空集,素数集合P完全建立;
⑥间接输出集合P中的元素,且每10个一行。

程序清单:
program erato;
const n=100;
type sss=set of 2..n;
var s, p : sss;
next , j : integer;
Begin
s:=[2..n]; {初始准备}
p:=[ ];
next:=2;
repeat {建立素数表}
while not (next in s) do next:=next+1;
p:=p+[next];
j:=next;
while j<=n do begin {去掉选定素数的倍数}
s:=s-[j];
j:=j+next;
end; {while}
until s=[ ];
j:=0;
for next:=2 to n do {输出素数集合元素}
if next in p then begin
write(next : 5);
j:=j+1;
if j=10 then {每10个素数为一行}
begin
writeln;
j:=0;
end;
end; {if}
End.
§6.2 记录类型
记录类型数据是由固定数量,具有不同类型的成份组成。

这种数据在实际问题中常遇到,如描述学生姓名、性别、年龄、班级和各科成绩的档案登记表。

这种数据用数组类型是非常烦琐的,可以利用Pascal提供的记录类型。

§6.2.1 记录类型的数据
记录是由固定数量的字段(又称域)的元素所组成的一种结构,各个字段可以具有各种不同的数据类型,每个字段都有一个名称即字段标识符。

记录类型定义的一般形式:
TYPE <类型标识符> = RECORD
<字段名1> : <类型1>;
┆┆
<字段名n> : <类型n>;
END;
记录中描述对象的字段表,包括了记录的固定部分和变体部分;记录的固定部分由字段名和类型说明部分,记录的变体部分在本节的最后介绍。

下面介绍如何描述记录的数据,例如:
type date = record
year : 1900 .. 2500;
month : (JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC); day : 1..31;
end;
var date1, date2 : date;
对记录类型变量的访问,不同于数组那样通过下标来访问其成份,而是通过记录变量名,句号(.),字段名来访问记录中的成份,称为记录的点记法,其形式为:
<记录变量名>.<字段名> := <数据项>;
例如: date1.year := 1937;
date1.month := JUL;
date1.day:=7;
如果记录的某个字段是字符型数组,如字段是一个由20字符组成的字符型数组,则可用循环语句读入字符:
for i:=1 to 20 do read ([ i ]);
·记录类型与数组类型相似,允许在同类型的两个记录之间进行整体赋值。

如: date2 := date1;
·记录可以嵌套,即记录中的字段的类型也可以是记录,嵌套的记录类型是有层次的数据类型。

在同一层的标识符不能同名,但不同层的字段名可以同名。

例如: var r : integer;
s : record
r : real;
s : record
r : char;
s : boolean;
end;
end;
对于层次记录的引用必须采用自顶向下的完全路径,如:
var worker = record
age : 15..70;
birth : record
year : 1900..2200;
month : 1..12;
end;
end;
引用记录变量worker的域,表示出生年,应写成:
worker . birth . year
【例2】设学生成绩登记表有下列项目:学号、姓名、年龄、班级、数学、物理、政治、英语、总分。

现对学生成绩进行统计,算出各科的总分和平均分。

程序清单:
program stu;
const n=60;
type student=record
no : integer;
name : string[16];
age : 6..30;
class : string[8];
math,physics,politics,english : 0..100;
tal : 0..400;
end;
var st : array[1..n] of student;
i,j,summ,sumph,sumpl,sume : integer;
Begin
for i:=1 to n do begin
readln(st[i].no,st[i].name);
readln(st[i].age,st[i].class);
readln(st[i].math,st[i].physics,st[i].politics,st[i].english);
st[i].tal:=st[i].math+st[i].physics+st[i].politics+st[i].english;
end;
summ:=0; sumph:=0; sumpl:=0; sume:=0;
for i:=1 to n do begin
summ:=summ+st[i].math;
sumph:=sumph+st[i].physics;
sumpl:=sumpl+st[i].politics;
sume:=sume+st[i].english;
end;
writeln('math ',summ,' average ',summ/n);
writeln('physics ',sumph,' average ',sumph/n);
writeln('politics ',sumpl,' average ',sumpl/n);
writeln('english ',sume,' average ',sume/n);
End.
§6.2.2 with语句
从上例中可见,用点记法引用记录会使句子冗长,若能像存取简单变量一样存取记录的字段,则会使之简便得多。

开域语句正好提供了这种功能,它“打开一个记录”后便可像引用变量那样使用字段名。

开域语句的一般形式为:
with <记录变量名> do <语句>;
其中do后面的语句可以是简单语句,也可以是复合语句,在这些语句中,只要使用字段名就可以,不必再在前面写上记录变量名。

例如,给记录date1赋值,不用前面的点记法,而用开域语句,则为:
with date1 do begin
year := 1937;
month := Jul;
day := 7;
end;
【例3】下面是用with语句对例2的改写。

program stu;
……
Begin
for i:=1 to n do
with st[i] do begin
readln(no, name);
readln(age, class);
readln(math, physics, politics, english);
tal:=math+physics+politics+english;
end;
summ:=0; sumph:=0; sumpl:=0; sume:=0;
for i:=1 to n do
with st[i] do begin
summ:=summ+math;
sumph:=sumph+physics;
sumpl:=sumpl+politics;
sume:=sume+english;
end;
writeln('math ',summ,' average ',summ/n);
writeln('physics ',sumph,' average ',sumph/n);
writeln('politics ',sumpl,' average ',sumpl/n);
writeln('english ',sume,' average ',sume/n);
End.
§6.2.3 变体记录
上面介绍的记录的元素,其数量和类型都是固定的,但在许多数据处理问题中,有时希望元素的数量及其类型能有所不同。

例如在学生档案中,政治面目这一栏,每个学生的情况可能不同,如共产党员填入党年份,共青团员填入团年份,一般学生什么都不用填。

记录的变体部分的一般形式:
TYPE <类型标识符> = RECORD
<字段名1> : <类型1>;
┆┆
<字段名n> : <类型n>;
CASE <标志字段> : <类型标识符> OF
<标号1> : (<字段表1>);
┆┆
<标号n> : (<字段表n>);
END;
其中每个变体由一个标号表和一个字段表构成。

标号表是一个标号和用逗号分隔的标号序列,这些标号都是标志字段的值。

一个记录变量将选中哪一个变体是由变体部分中标志字段的值确定的,当标志字段的值(只能是枚举型、字符型等有序类型的常量)等于某一变体的标号时,则这个变体被选中。

访问记录变量变体部分的一个元素类似于访问固定部分中的一个元素。

例如:
TYPE STATUS = (P, M, D, S);
PERSON= RECORD
name : string[20];
sex : char;
case politics : STATUS of
P : (pdate : 1900..2500 );
M: (mdate : 1900..2500);
S : ( );
END;
var p : PERSON;
则可进行赋值:
:=’zhu ming’;
p.politics:=P;
p.date:=1999;
使用变体记录要注意以下几点:
①标志字段的类型是有序类型,其标识符必须预先定义过;
②记录的固定部分必须放在变体部分之前;
③变体部分的case不同于一般的case语句,不需end匹配。

【例4】编制一个关于某科研小组成员年龄、学位情况的程序,进行处理和输出。

解:因为学位情况复杂,可能无学位,也可能是学士、硕士或博士,如果是博士还要考虑获得学位的时间、地点;如果是硕士和学士,要填入获得学位的时间。

程序清单:
program deg;
type
degree = ( d, m, q, n);
techer = record
i : integer;
age : 20..70;
case status : degree of
d : (year1: 1900..2500; stat
e : string[100] );
m: (year2 : 1900..2500);
d : (year3 : 1900..2500);
n : ( );
end;
var s: teacher;
ch : char;
k : integer;
begin
wrigteln (‘input age, degree, date, ocation:’);
for k:=1 to 100 do
with s do begin
i:=k;
readln(age);
readln(ch);
case ch of
‘d’ : status:=d;
‘m’ : status:=m;
‘q’ : status:=q;
‘n’ : status:=n;
end; {case}
case status of
d : begin
readln( year1, state);
writeln ( i : 5 , age : 6 , ‘ ‘, year1 : 7 , ‘ ‘, state);
end;
m : begin
readln (year2);
writeln ( i : 5 , age : 6 , ‘ ‘, year2 : 7);
end;
q : begin
readln (year3);
writeln ( i : 5 , a ge : 6 , ‘ ‘, year3 : 7);
end;
n : writeln ( i : 5 , age : 6 , ‘ other‘);
end; {case}
end; {with}
end.
练习六
1. 下列哪组类型的变量可以作为for循环中的循环控制变量()
A.枚举型和实数型 B. 枚举型和记录型变量中的子界型域
C.字符型和集合型 D. 子界型和记录型
2.下列类型定义中,合法的是()
A.TYPE p=(1,2,3,4) B. TYPE p=(‘a’, ‘b’, ‘c’, ‘d’)
C.TYPE p=(1..4) D. TYPE p=(a,b,c,d)
3. 摸球游戏:已知黑盒中的球为红、黄、蓝、白、黑五种颜色,从黑盒中依次取出三个球,若这三个球颜色互不相同,则可获奖。

请求出取三种颜色的球的所有可能取法。

(用枚举类型)
4.从键盘读入一个字符,判断:若为数字,则输出“digits”;若为小写字符,则输出“lower-letter”;若为大写字符,则输出”upper-letter”;若为其它字符,则输出“special characters”。

(用子界类型作为case语句标号)
5.从键盘读入年、月、日,输出该日期是当年的第几天。

(用子界类型)
6.下列表达式中运算结果为true的是()
A.[2,4,6]<>[6,4,2] B. [1,2,3,4]<=[1..4]
C. 7 in [2,4,6,8]
D. [2,4,6]+[2,4,6]=[2,2,4,4,6,6]
7. 下列有关集合运算的表达式中,有语法错误的是()
A.‘Y’ in [‘C’, ‘D’, ‘K’.. ‘L’, ‘Z’] B. [2,4,6]*[8,5,2] C.[‘L’, ‘M’, ‘N’]+[3,6,9] D. [1..100]<>[1..211]
8.用集合类型实现筛法求质数。

9
假设你为小组长,编程完成下列功能:
①输入学生的成绩表,其中总评成绩由程序算出
②按总评成绩从高到低打印学生名单
10.指出下列类型定义中的错误:
(1) TYPE rec1=RECORD
case kind : real of
1 : (color : (red,blue,green));
5 : (p : Boolean ; x : integer);
END
(2) TPYE rec2=RECORD
name : string[20];
sex : (male , female);
case i : 1..3 of
1 : (math : integer);
2 : (phys : integer);
3 : (english : integer);
END;
11.设有说明:
TYPE
date=RECORD
year : 0..9999;
month : 1..12;
day : 1..31
END;
pers=RECORD
name : array [1..16] of char;
birthdate : date;
sem : (male , female);
END;
VAR aa : pers;
下列操作中错误的是()
A. read (aa.birthdate.year);
B. with aa do
Read (birthdate.year);
C. with aa.birthdate do
Read (birthdate.year);
D. with aa.birthdate do
Read (year);
12.平面上的点由笛卡儿直角坐标系给出,建立描述平面上一点位置的数据类型,并编一个过程,求任意三点构成的三角形的外接圆。

输入: x1 y1
x2 y2
x3 y3
输出: x0 y0 R (x0、y0为圆心坐标,R 为圆的半径)
11。

相关文档
最新文档