冲刺NOIP试题四.docx

上传人:b****7 文档编号:10430027 上传时间:2023-02-11 格式:DOCX 页数:19 大小:56.21KB
下载 相关 举报
冲刺NOIP试题四.docx_第1页
第1页 / 共19页
冲刺NOIP试题四.docx_第2页
第2页 / 共19页
冲刺NOIP试题四.docx_第3页
第3页 / 共19页
冲刺NOIP试题四.docx_第4页
第4页 / 共19页
冲刺NOIP试题四.docx_第5页
第5页 / 共19页
点击查看更多>>
下载资源
资源描述

冲刺NOIP试题四.docx

《冲刺NOIP试题四.docx》由会员分享,可在线阅读,更多相关《冲刺NOIP试题四.docx(19页珍藏版)》请在冰豆网上搜索。

冲刺NOIP试题四.docx

冲刺NOIP试题四

全国青少年信息学奥林匹克

联赛复赛模拟试题

湖南省长沙市第一中学周祖松

试题名称

infinit

remove

game

fire

infinit

remove

game

fire

输入文件名

infinit.in

remove.in

game.in

fire.in

输出文件名

infinit.out

remove.out

game.out

fire.out

试题类型

非交互式程序题

非交互式程序题

非交互式程序题

非交互式程序题

附加文件

时限

0.1秒

0.1秒

0.1秒

0.1秒

1.无限序列

(infinit.pas/c/cpp)

【问题描述】

我们按以下方式产生序列:

1、开始时序列是:

"1";

2、每一次变化把序列中的"1"变成"10","0"变成"1"。

经过无限次变化,我们得到序列"1011010110110101101..."。

总共有Q个询问,每次询问为:

在区间A和B之间有多少个1。

任务写一个程序回答Q个询问

输入第一行为一个整数Q,后面有Q行,每行两个数用空格隔开的整数a,b。

输出共Q行,每行一个回答

约定

∙1<=Q<=5000

∙1<=a<=b<263

样例

infinit.in

infinit.out

1

28

4

分析:

我们先看看序列变化规律,S1="1",S2="10",S3="101",S4="10110",S5="10110101",等等.Si是S(i+1)的前缀。

序列Si是由序列S(i-1)和S(i-2),连接而成的。

即Si=Si-1+Si-2(实际上上是Fibonacci数列)。

找到规律以后,我们可以可以用递归的方法求出从从位置1到位置X之间所有的1的个数,用一个函数F计算,结果为f(b)-f(a-1)。

时间复杂度为:

O(Q*logMAX_VAL)

此题需要先找出数学规律,再进用递归实现。

主要考查选手的数学思维能力和递归程序的实现。

源程序:

const

nn=92;//进行92次的数列扩展后,数列长度就会超过给定的数据范围,

var

f,ft:

array[0..nn]ofint64;

q,i,j,l1,l2:

longint;

a,b:

qword;

procedureprapre;{预处理}

vari:

longint;

begin

f[0]:

=1;f[1]:

=1;

ft[0]:

=0;ft[1]:

=1;

fori:

=2tonndo

begin

f[i]:

=f[i-1]+f[i-2];

ft[i]:

=ft[i-1]+ft[i-2];

end;

end;

functionfind(a:

int64;ll:

longint):

int64;{求这个数列的前a个有多少个1}

begin

ifa=0thenexit(0);

find:

=0;

ifa=f[ll]thenfind:

=ft[ll]else

ifa<=f[ll-1]thenfind:

=find(a,ll-1)

elsefind:

=ft[ll-1]+find(a-f[ll-1],ll-2);

end;

begin

assign(input,'infinit.in');reset(input);

assign(output,'infinit.out');rewrite(output);

prapre;

readln(q);

fori:

=1toqdo

begin

readln(a,b);

writeln(find(b,nn)-find(a-1,nn));

end;

close(input);close(output);

end.

 

2.删数

(remove.pas/c/cpp)

【问题描述】

有N个不同的正整数数x1,x2,...xN排成一排,我们可以从左边或右边去掉连续的i个数(只能从两边删除数),1<=i<=n,剩下N-i个数,再把剩下的数按以上操作处理,直到所有的数都被删除为止。

每次操作都有一个操作价值,比如现在要删除从i位置到k位置上的所有的数。

操作价值为|xi–xk|*(k-i+1),如果只去掉一个数,操作价值为这个数的值。

任务

如何操作可以得到最大值,求操作的最大价值。

InputData

输入文件remove.in的第一行为一个正整数N,第二行有N个用空格隔开的N个不同的正整数。

OutputData

输出文件remove.out包含一个正整数,为操作的最大值

约束和提示

3<=N<=100

N个操作数为1..1000之间的整数。

样例

remove.in

6

542919621133118

remove.out

768

说明,经过3次操作可以得到最大值,第一次去掉前面3个数54、29、196,操作价值为426。

第二次操作是在剩下的三个数(21133118)中去掉最后一个数118,操作价值为118。

第三次操作去掉剩下的2个数21和133,操作价值为224。

操作总价值为426+118+224=768。

分析:

这是一个基本的动态规划问题

我们用F[i][j]表示按规则消去数列a[i..j]得到的的最大值;

删除第i个数得到的最大值为a[i];

删除a[i..j]得到的最大值为:

一次性删除数列a[i..j]得到的值是|a[i]-a[j]|*(j-i+1)或者是先删除a[i..k]再删除a[k+1..j],k在i到j-1之间,得到的值是F[i][k]+F[k+1][j].

我们得到状态转移方程:

F[i][i]=a[i],fori=1..N

对于任意的i

F[i][j]=max{|a[i]-a[j]|*(j-i+1),f[i][i]+f[i+1][j],F[i][i+1]+F[i+2][j],...,F[i][j-1]+F[j][j]}

F[1,n]为所求的解

时间复杂度:

o(N^3);

此题需要选手对算法的时间复杂度进行分析,简单的搜索是不能在规定时间内求出结果,必需使用高效的算法,主要考查选手运用高效算法——动态规划解决问题的能力。

源程序:

var

n:

longint;

a:

array[1..100]oflongint;

f:

array[0..101,0..101]oflongint;

procedureinit;

vari:

longint;

begin

readln(n);

fori:

=1tondoread(a[i]);

end;

functionwork(i,k:

longint):

longint;

begin

ifi=kthenexit(a[i]);

exit(abs(a[i]-a[k])*(k-i+1));

end;

functionmax(a,b:

longint):

longint;

begin

ifa>bthenmax:

=aelsemax:

=b;

end;

proceduremain;

vari,j,p:

longint;

begin

fillchar(f,sizeof(f),0);

fori:

=1tondof[i,i]:

=a[i];

fori:

=ndownto1do

forj:

=i+1tondo

begin

forp:

=i+1toj+1do

f[i,j]:

=max(f[i,j],f[p,j]+work(i,p-1));

forp:

=j-1downtoi-1do

f[i,j]:

=max(f[i,j],f[i,p]+work(p+1,j));

end;

end;

procedureprint;

begin

writeln(f[1,n]);

end;

begin

assign(input,'remove.in');reset(input);

assign(output,'remove.out');rewrite(output);

init;

main;

print;

close(input);close(output);

end.

3.俄罗斯方块

(game.pas/c/cpp)

【问题描述】

相信大家都玩过“俄罗斯方块”游戏吧,“俄罗斯方块”是一个有趣的电脑小游戏,现有一个有C列、行不受限定游戏平台,每一次下落的方块是下列的7个图形的一种:

在下落的过程中,游戏者可以作90、180或270度旋转,还可以左右移动,对于每一次方块落地,我们要求方块的每一部分都必须与地面(最底面或己落下的方块上表面)接触,例如,有一个宽度为6列的平台,每一列的初始高度(已经占用的方格数)分别为2,1,1,1,0和1。

编号为5的方块下落,有且仅有5种不同的落地方法:

现给出每一列的初始高度和下落方块的形状,请你编写一个程序,求出落地的方法总数,也就是落地后,地表面形成的不同的形状总数。

输入:

第一行为二个整数C和P,1≤C≤100,1≤P≤7,表示列数和下落方块的编号

第二行共有用一个空隔隔开的C个整数,每一个数字在0到100,之间(包含0和100),表示每一列的初始高度

输出:

输出为一个整数,表示落地的方法总数

样例数据

Input1

65

211101

Output1

5

Input2

51

00000

Output2

7

Input3

94

435465766

Output3

1

分析:

我们用编码序列表示下落方块的底部,比如编号为5的方块经过4种旋转得到4个序列:

{1,1,1},{1,2},{2,1,2}和{2,1}。

如果序列A上每一个元素都加上同一个常数得到序列B,我们就称序列A和序列B是对应的,比如序列{2,1,2}与{5,4,5}对应,但不与{4,5,4}或{5,4}对应。

可以看出,当且仅当方块的序列与地面相对位置的高度形成的序列对应,那么这个方块能与地面能结合。

因此对于给定的方块和地面,我们只要简单地检查所有旋转所形成的可能的形状,这样我们就可以用枚举法求解。

此题比较简单,用枚举法解决,在实现中要考虑各种变换。

主要考查选手的基本的程序实现能力。

源程序:

programgame;

constMAXS=100;

fin='game.in';

fout='game.out';

var

v:

array[1..MAXS]oflongint;

s,f,i,count:

longint;

begin

assign(input,fin);reset(input);

assign(output,fout);rewrite(output);

readln(s,f);

if(f=4)or(f=7)then{如果是第四种和第七种,则变成第三种和第六种}

begin

dec(f);

fori:

=1tosdoread(v[s-i+1]);

endelse

fori:

=1tosdoread(v[i]);

count:

=0;

casefof

1:

begin

count:

=s;

fori:

=1tos-3do

if(v[i]=v[i+1])and(v[i]=v[i+2])and(v[i]=v[i+3])theninc(count);

end;

2:

begin

fori:

=1tos-1do

ifv[i]=v[i+1]theninc(count);

end;

3:

begin

fori:

=1tos-1do

ifv[i]=v[i+1]+1theninc(count);

fori:

=1tos-2do

if(v[i]=v[i+1])and(v[i]=v[i+2]-1)theninc(count);

end;

5:

begin

fori:

=1tos-2do

if((v[i]-v[i+1]=1)or(v[i]-v[i+1]=0))and(v[i]=v[i+2])theninc(count);

fori:

=1tos-1do

ifabs(v[i]-v[i+1])=1theninc(count);

end;

6:

begin

fori:

=1tos-2do

if(v[i+1]=v[i+2])and((v[i+1]-v[i]=1)or(v[i+1]-v[i]=0))theninc(count);

fori:

=1tos-1do

if(v[i]=v[i+1])or(v[i]=v[i+1]+2)theninc(count);

end;

end;

writeln(count);

close(input);

close(output);

end.

 

4.燃烧木棍

(fire.pas/c/cpp)

【问题描述】

Tom是调皮的孩子,特别喜欢玩火,现在他手上有若干根长度分别为1和

的木棍,还有一张不能燃烧的平板,他把平板划分成长度为1的单元格,并标上座标。

然后按以下规则把平板上的木棍组成一个连通图:

木棍的两端必须放在单元格的顶点上。

即长度为1的木棍放在单元格的某一边上,长度为

的木棍放在单元格的对角线上。

Tom的点火规则是,只能从木棍的两端点火,而不能从木棍的中间点火。

如图,不能在A点点火,但在C点或B点点火都是充许的。

点火后,火会沿着木棍向前燃烧(每根都有自己的燃烧速度),并能点燃与它相接的其它木棍。

任务:

写一程序计算从哪里开始点火,使得燃烧的总时间最短,输出最短时间。

InputData

输入文件bete.in第一行为一个正整数N,表示组成图形的木棍数目,后面共有N行,每行5个数:

X1Y1X2Y2T,其中(X1,Y1)和(X2,Y2)分别表示木棍两端的坐标,T表示木棍燃烧时间,是指从木棍的某一端点火燃烧到别一端,燃完所需的时间。

OutputData

输出文件bete.out是一个保留4位小数的实数,表示所有木棍完全燃烧的最少时间。

约束1≤n≤40

保证图形是连通的,所有的木棍长度都是1或

,没有任何两根木棍重合.

-200≤X1,Y1,X2,Y2≤200;0≤T≤107.

如果你的输出结果与标准答案相差小于0.001,则认为你的结果正确。

样例1

fire.in

fire.out

解释

1

00111

1.0000

从任一端点火都行,燃烧时间都是1

样例2

fire.in

fire.out

解释

5

00011

100110

00101

00111

22111

3.2500

在(0,0)位置点火

木棍1,3和4将被点燃,燃烧0.5分钟后,木棍2将被从中间点燃向两端燃烧,再过0.5分钟,木棍1,3,4将被完全燃烧,木棍5将被点燃并在1分钟后燃烧完(比木棍2早燃完)。

木棍2从中间向两端燃烧0.5分钟以后,变成两小段,每段的燃烧时间是4.5分钟。

但因为此时两小段木棍的另一端也同时被点燃,燃烧速度变成原来的两倍,还需2.25分钟的燃烧时间,所以总时间:

1+2.25=3.25

样例3

fire.in

fire.out

解释

3

111210

122210

112250

35.0000

在(1,2)位置点火,木棍(11,12)和(12,22)将燃烧10分钟。

.最后一根木棍在10分钟后从两端被点燃,燃烧时间为25分钟。

分析:

在此题问题中,木棍构成的是一个连通图,如果我们直接构图处理比较复杂,我们对原问题进行转换,由于木棍与木棍之间只能在木棍的两端或中间相交。

我们把每根木棍拆分成两根相等的小木棍,这样,木棍的数量增加了一倍,原问题就转化为,木棍与木棍之间只能在木棍的两端相交,这样处理起来就比较方便。

我们以木棍为边,木棍与木棍之间的交点为顶点,构建一个连通图,问题变为寻找一个合适的顶点,使得点燃以后完全燃烧的时间最短。

很显然,燃烧时间等于点燃的顶点到图中最远点的时间,如下图

 

我们可以先求出某个点到其它所有点的最短时间,在这里我们可以使用Floyd’s算法求出任意两点之间的最短时间,时间复杂度为O(N3)。

注意,我们在这里用Floyd’s算法求的是第个点被点燃的时间,每个点被点燃,并不代表所有木棍都被完全燃烧,如上图。

求出从某个点到其它点的最短时间以后,余下的问题是检查每一边是否完全燃烧。

如果没有完全燃烧,求出剩余边燃烧所需最长时间。

对于燃烧时间为L的木棍,它的两端被点燃的时刻为T1和T2,如果T1=T2+L或者是T2=T1+L,那么燃烧到T1和T2的最大时刻,这根木棍己经完全燃烧。

如果T1与T2之间的时间差不等于L,那么就说明火是从不同的路径燃烧到这根木棍的两端。

火将从两端向中间燃烧,并在木棍内的某个点燃完,在简单情况中,如果是从两端同时点燃,燃烧时间为L/2。

更一般地,如果T1与T2不等,我们设一端是从0时刻点燃,另一端是从T时刻点燃,那么这根木棍的燃烧时间为T+(L-T)/2.

此题比较复杂,先要构图,并且要对原图形进行转换,再用Floyd’s算法求出任意两点之间的最少时间,最后还要检查所有的边是否燃烧,此题考查选手用图解决问题的能力,并且此题不能简单的套用Floyd’s算法,需要选手在解决问题中灵活运用经典算法。

源程序:

ProgramMatches;

Const

MaxN=42;{N根木棍的端点}

MaxG=2*MaxN+1;{拆分以后的最大顶点数}

Infinity=MaxLongInt;{燃烧时间的最大值}

VarN:

Integer;

Match:

Array[1..MaxN]OfRecord{每根木棍构成边的信息:

端点座标和燃烧时间}

X1,Y1,X2,Y2:

Integer;

Time:

LongInt;

End;

NG:

Integer;

Vertex:

Array[1..MaxG]OfRecord

X,Y:

Integer;{图的顶点信息}

End;

Edge,Distance:

Array[1..MaxG,1..MaxG]OfLongInt;

Res:

Extended;{燃烧最少时间}

ResX,ResY:

Integer;

ProcedureInit;{输入}

VarI:

Integer;

Begin

Read(N);

ForI:

=1ToNDo

WithMatch[I]Do

Read(X1,Y1,X2,Y2,Time);

End;

FunctionGetVertex(VX,VY:

Integer):

Integer;

{intoarcenumarulvarfuluicucoordonateledatedacalipseste,estecreat}

VarI:

Integer;

Begin

ForI:

=1ToNGDo

WithVertex[I]Do

If(X=VX)And(Y=VY)ThenBegin

GetVertex:

=I;

Exit;

End;

Inc(NG);{Dacavarfulnuestegasit}

WithVertex[NG]DoBegin

X:

=VX;

Y:

=VY;

ForI:

=1ToNG-1DoBegin

Edge[I,NG]:

=Infinity;

Edge[NG,I]:

=Infinity;

End;

Edge[NG,NG]:

=0;

End;

GetVertex:

=NG;

End;

ProcedureAddEdge(X1,Y1,X2,Y2:

Integer;Time:

Longint);

{Functiaadaugamuchieintrevarfuri}

VarA,B:

Integer;

Begin

A:

=GetVertex(X1,Y1);

B:

=GetVertex(X2,Y2);

Edge[A,B]:

=Time;

Edge[B,A]:

=Time;

End;

ProcedureBuildGraph;{构图}

VarI:

Integer;

Begin

NG:

=0;

ForI:

=1ToNDo

WithMatch[I]DoBegin

AddEdge(X1*2,Y1*2,X1+X2,Y1+Y2,Time);

AddEdge(X1+X2,Y1+Y2,X2*2,Y2*2,Time);

End;

End;

ProcedureFindShortestPaths;{Floyd’s算法求任意两点之间的最短时间}

VarK,I,J:

Integer;

Begin

Distance:

=Edge;

ForK:

=1ToNGDo

ForI:

=1ToNGDoIfDistance[I,K]

ForJ:

=1ToNGDoIfDistance[K,J]

IfDistance[I,K]+Distance[K,J]

Distance[I,J]:

=Distance[I,K]+Distance[K,J];

End;

FunctionBurnAt(At:

Integer):

Extended;

VarI,J:

Integer;

Curent,ThisEdge:

Extended;

Begin

Curent:

=0;

ForI:

=1ToNGDoIfDistance[At,I]>CurentThenCurent:

=Distance[At,I];

ForI:

=1ToNGDo

ForJ:

=I+1ToNGDoIfEdge[I,J]

If(Distance[At,I]

(Distance[At,J]

IfDistance[At,I]

ThisEdge:

=Distance[At,J]+(Edge[I,J]

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 总结汇报 > 实习总结

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1