安阳一中2009-2010学年第一学期
信息学奥赛期初考试2008级(高二)试
时间 2009年8月23日 15:00~18:00
题目一览:
题目名称
锻炼
细胞
飞翔
单词划分
提交文件
exercise.pas
cell.pas
fly.pas
word.pas
输入文件
exercise.in
cell.in
fly.in
word.in
输出文件
exercise.out
cell.out
fly.out
word.out
分 值
100
100
100
100
时间限制
1s
1s
1s
1s
题一 锻炼计划(exercise.pas)
问题描述
身体是革命的本钱, OIers 不要因为紧张的学习和整天在电脑前而忽视了健康问题。小 x 设计了自己的锻炼计划,但他不知道这个计划是否可行,换句话说如果计划不当可能会让他的体力超支,所以小 x 请你帮助他。
一天有 1440 分钟,所以小 x 列出的是这一整天第 1 至第 1440 分钟的计划。小 x 的体力用一个整数来
示,他会按照计划表进行锻炼,同时,每分钟小 x 的体力会自动增加 1 。如果某一分钟末小 x 的体力小于等于零,那么可怜的小 x 就累死了 ……
输入(exercise.in)
第一行是用空格分开的两个整数 n,m ,分别表示小 x 的初始体力值和计划的项目数量。从第二行开始的 m 行,每行描述一个锻炼项目:名称、开始时间 a 、结束时间 b 、每分钟耗费的体力 ( 用空格分隔 ) ,表示此项目从第 a 分钟初开始,第 b 分钟末结束。锻炼项目按照开始时间递增顺序给出,不会出现两个项目时间冲突的情况。
输出(exercise.out)
输出包括两行,如果计划可行,第一行输出 "Accepted" ,第二行输出这一天过后最后剩余的体力;否则在第一行输出 "Runtime Error" ,第二行输出在第几分钟累死。
样例1
exercise.in
exercise.out
10 1
Basketball 1 10 1
Accepted
1440
样例2
exercise.in
exercise.out
1 1
Nunchakus 1 1 2
Runtime Error
1
数据范围
0
分析:
由于时间只有1440分钟,所以可以直接模拟
使用一个1..1440的线性数组delta,delta[i]表示第i分钟体力的改变量
delta[i]的初始值为1,表示每分钟体力恢复1
对于每一个计划的起止时间(t1, t2),把delta[t1..t2]的值减去此计划每分钟耗费的体力值
本题主要考查字符串处理和数组应用等编程基础,送分题。
参考程序:
const
fi='exercise.in';
fo='exercise.out';
var
ch:char;
n,m,i,j:longint;
start,ed,cost:longint;
delta:array[1..1440] of longint;
died:boolean;
begin
assign(input,fi);
reset(input);
assign(output,fo);
rewrite(output);
for i:=1 to 1440 do
delta[i]:=1;
readln(n,m);
for i:=1 to m do
begin
repeat
read(ch);
until ch=' ';
readln(start,ed,cost);
for j:=start to ed do
dec(delta[j],cost);
end;
died:=false;
for i:=1 to 1440 do
begin
inc(n,delta[i]);
if n<=0 then
begin
writeln('Runtime Error');
writeln(i);
died:=true;
break;
end;
end;
if not died then
begin
writeln('Accepted');
writeln(n);
end;
close(output);
close(input);
end.
题二 细胞(cell.pas)
问题描述
一矩形阵列由数字0到9组成,数字1到9代表细胞,细胞的定义为沿细胞数字上下左右还是细胞数字则为同一细胞,求给定矩形阵列的细胞个数。如阵列:
0234500067
1034560500
2045600671
0000000089
有4个细胞。
输入(cell.in)
整数m,n(m行,n列,m<=50 n<=80)矩阵
输出(cell.out)
一个整数,表示细胞的个数。
样例
cell.in
cell.out
4 10
0234500067
1034560500
2045600671
0000000089
4
数据范围
40%的数据,m,n<=20
100%的数据,m<=50 ,n<=80
问题分析:
这是个简单的图的填充问题,要求计算封闭区域的个数,使用广搜或深搜填充每一个封闭区域,并统计其个数就行了。主要考查搜索算法的灵活运用。
这里需要注意的是,如果使用广搜来填充,队列数组不需要每次都初始化,如果数组很大的话会浪费较多时间,有同学的程序超时就是数组开得太大,并且每次都初始化数组,即使使用fillchar,对大的数组进行多次的初始化也是十分耗时的。
参考程序:
const dx:array[1..4] of -1..1=(-1,0,1,0);
dy:array[1..4] of -1..1=(0,1,0,-1);
var int:text;
name,s:string;
pic:array[1..50,1..80] of byte;
bz:array[1..50,1..80] of boolean;
m,n,i,j,num:integer;
h:array[1..4000,1..2] of byte;
procedure floodfill(p,q:integer);
var i,head,tail,x,y:integer;
begin
inc(num);bz[p,q]:=false;
head:=1;tail:=1;h[1,1]:=p;h[1,2]:=q;{遇到的第一个细胞入队}
repeat
for i:=1 to 4 do{沿细胞的上下左右四个方向搜索细胞}
begin
x:=h[head,1]+dx[i];y:=h[head,2]+dy[i];
if (x>0) and (x<=m) and (y>0) and (y<=n) and bz[x,y]
then begin inc(tail);h[tail,1]:=x;h[tail,2]:=y;bz[x,y]:=false;end;{为细胞的入队}
end;
inc(head);{队头指针加1}
until head>tail;{直至队空为止}
end;
begin
fillchar(bz,sizeof(bz),true);num:=0;
assign(input,'cell.in');
assign(output,'cell.out');
reset(input);
rewrite(output);
readln(m,n);
for i:=1 to m do
begin readln(s);
for j:=1 to n do
begin pic[i,j]:=ord(s[j])-ord('0');
if pic[i,j]=0 then bz[i,j]:=false;
end;
end;
for i:=1 to m do
for j:=1 to n do if bz[i,j] then floodfill(i,j);{在矩阵中寻找细胞}
writeln(num);
close(input);
close(output);
end.
题三 飞翔(fly.pas)
问题描述
鹰最骄傲的就是翱翔,但是鹰们互相都很嫉妒别的鹰比自己飞的快,更嫉妒其他的鹰比自己飞行的有技巧。于是,他们决定举办一场比赛,比赛的地方将在一个迷宫之中。
这些鹰的起始点被设在一个N*M矩阵的左下角map[1,1]的左下角。终点被设定在矩阵的右上角map[N,M]的右上角,有些map[i,j]是可以从中间穿越的。每一个方格的边长都是100米。如图所示:
没有障碍,也没有死路。这样设计主要是为了高速飞行的鹰们不要发现死路来不及调整而发生意外。潘帕斯雄鹰冒着减RP的危险从比赛承办方戒备森严的基地中偷来了施工的地图。但是问题也随之而来,他必须在比赛开始之前把地图的每一条路都搞清楚,从中找到一条到达终点最近的路。(哈哈,笨鸟不先飞也要拿冠军)但是此鹰是前无古鹰,后无来鹰的吃菜长大的鹰--菜鸟。他自己没有办法得出最短的路径,于是紧急之下找到了学OI的你,希望找到你的帮助。
输入(fly.in)
首行为n,m,第2行为k表示有多少个特殊的边。以下k行为两个数,i,j表示map[i,j]是可以直接穿越的。
输出(fly.out)
仅一行,1,1-->n,m的最短路径的长度,四舍五入保留到整数即可
样例
fly.in
fly.out
3 2
3
1 1
3 2
1 2
383
数据范围
20%的数据,0
标准 的思想列出方程
f[i,j]表示在map[i,j]到map[1,1]的最小值
则:f[i,j]=min{f[i-1,j],f[i,j-1]}+1 如果存在特殊边再这样处理一下
f[i,j]=min{f[i,j],f[i-1,j-1]+sqrt(2)}
ans=round(f[n,m])即可
但是这样就有两个问题,一个是时间复杂度问题O(nm)在这个题目中是TLE的,另一个问题是空间复杂度问题O(nm)的空间在这个题目中是MLE的
那让我们换一个思路,如果能走特殊路,必然要走特殊路,这个是显然的。
从map[i,j]到map[i+1,j+1]最多有三条路,其中的两条长度为2,而如果有特殊路,其长度为sqrt(2),
而这里k只有1000,在这1000个路中寻找最长的可走路线,即lis的变形问题。这个复杂度是O(k^2)。
实现方式,首先字典排序,把边的顺序处理好,然后lis,最后利用数学推导在取x条特殊路的时候所走的路线即可。
并且题目本身并不难。本题其实有一个提示,M、N都是非常大的数,如果从m、n去考虑只有O(M+N)的算法能够AC,但是K十分的小,这就是提示,让大家往K的复杂度上想。
参考程序:
program ural1119;
const maxn=1000;
var
f:array[0..maxn] of longint;
a:array[0..maxn,1..2] of longint;
i,j,k,m,n,temp:longint;
fil:text;
procedure init;
begin
assign(fil,'fly.in');
reset(fil);
readln(fil,n,m);
readln(fil,k);
for i:=1 to k do
readln(fil,a[i,1],a[i,2]);
close(fil);
for i:=1 to k-1 do
for j:=1 to k-i do
if (a[j,1]>a[j+1,1]) or (a[j,2]>a[j+1,2]) then
begin
temp:=a[j,1];
a[j,1]:=a[j+1,1];
a[j+1,1]:=temp;
temp:=a[j,2];
a[j,2]:=a[j+1,2];
a[j+1,2]:=temp;
end;
end;
procedure main;
begin
for i:=1 to k do
begin
temp:=0;
for j:=0 to i do
begin
if (a[j,1]