NOIP提高组试题及解析.docx

上传人:b****6 文档编号:8459912 上传时间:2023-01-31 格式:DOCX 页数:12 大小:31.58KB
下载 相关 举报
NOIP提高组试题及解析.docx_第1页
第1页 / 共12页
NOIP提高组试题及解析.docx_第2页
第2页 / 共12页
NOIP提高组试题及解析.docx_第3页
第3页 / 共12页
NOIP提高组试题及解析.docx_第4页
第4页 / 共12页
NOIP提高组试题及解析.docx_第5页
第5页 / 共12页
点击查看更多>>
下载资源
资源描述

NOIP提高组试题及解析.docx

《NOIP提高组试题及解析.docx》由会员分享,可在线阅读,更多相关《NOIP提高组试题及解析.docx(12页珍藏版)》请在冰豆网上搜索。

NOIP提高组试题及解析.docx

NOIP提高组试题及解析

NOIP2007提高组试题及解析

1.统计数字(count.pas/c/cpp)

【问题描述】

   某次科研调查时得到了n个自然数,每个数均不超过(1.5*109)。

已知不相同的数不超过10000个,现在需要统计这些自然数各自出现的次数,并按照自然数从小到大的顺序输出统计结果。

【输入】

   输入文件count.in包含n+1行;

   第一行是整数n,表示自然数的个数;

   第2~n+1每行一个自然数。

【输出】

   输出文件count.out包含m行(m为n个自然数中不相同数的个数),按照自然数从小到大的顺序输出。

每行输出两个整数,分别是自然数和该数出现的次数,其间用一个空格隔开。

【输入输出样例】

count.in

8

2

4

2

4

5

100

2

100

count.out

23

42

51

1002

【限制】

   40%的数据满足:

1<=n<=1000

   80%的数据满足:

1<=n<=50000

   100%的数据满足:

1<=n<=,每个数均不超过1500000000(1.5*109)

【解题思想1】显然,此题可以用排序的方法来解决,根据n的范围,可以看出,O(nlogn)的算法是可以接受的。

【解题思想2】维护一个二叉树,以数的大小作为节点的权值,以数的重复次数作为节点的附加信息。

之后中序遍历即可。

看起来,树内的节点个数应该不到n,所以可能表现不错,其算法复杂度依然为O(nlogn)

【实际数据规模】挺小的,而且也没有传说中的卡Qsort的数据,全部都很温柔。

【分析】这个题目实在不能说是一道TG组的好题。

实际上,个人认为题目最大的意义在于:

提供了10个测试排序算法的不怎么特别好的数据。

话说回来,此题是送分题,但是送分题送的这么水,考察的也就只有OIers的细心程度了。

在考试的时候,要相信有简单的题目,要相信有直接的算法。

在我的身边就有几个同学因为这个题目与一等失之交臂,这是最可惜的事情。

vara:

array[1..]oflongint;

i,j,k,m,n:

longint;

procedureqsort(s,t:

longint);

vari,j,mid,temp:

longint;

begin

i:

=s;j:

=t;mid:

=a[(s+t)div2];

whilei<=jdo

begin

  whilea[i]

  whilea[j]>middodec(j);

  ifi<=jthen

   begin

    temp:

=a[i];a[i]:

=a[j];a[j]:

=temp;

    inc(i);dec(j);

   end;

end;

ifi

ifj>sthenqsort(s,j);

end;

begin

readln(n);

fori:

=1tondoreadln(a[i]);

qsort(1,n);

a[n+1]:

=maxlongint;

k:

=1;

fori:

=2ton+1do

ifa[i]<>a[i-1]then

  beginwriteln(a[i-1],'',k);k:

=1;end

elsek:

=k+1;

end.

                                                  2.字符串的展开(expand.pas/c/cpp)

【问题描述】

   在初赛普及组的“阅读程序写结果”的问题中,我们曾给出一个字符串展开的例子:

如果在输入的字符串中,含有类似于“d-h”或者“4-8”的字串,我们就把它当作一种简写,输出时,用连续递增的字母获数字串替代其中的减号,即,将上面两个子串分别输出为“defgh”和“45678”。

在本题中,我们通过增加一些参数的设置,使字符串的展开更为灵活。

具体约定如下:

   

(1)遇到下面的情况需要做字符串的展开:

在输入的字符串中,出现了减号“-”,减号两侧同为小写字母或同为数字,且按照ASCII码的顺序,减号右边的字符严格大于左边的字符。

   

(2)参数p1:

展开方式。

p1=1时,对于字母子串,填充小写字母;p1=2时,对于字母子串,填充大写字母。

这两种情况下数字子串的填充方式相同。

p1=3时,不论是字母子串还是数字字串,都用与要填充的字母个数相同的星号“*”来填充。

   (3)参数p2:

填充字符的重复个数。

p2=k表示同一个字符要连续填充k个。

例如,当p2=3时,子串“d-h”应扩展为“deeefffgggh”。

减号两边的字符不变。

   (4)参数p3:

是否改为逆序:

p3=1表示维持原来顺序,p3=2表示采用逆序输出,注意这时候仍然不包括减号两端的字符。

例如当p1=1、p2=2、p3=2时,子串“d-h”应扩展为“dggffeeh”。

   (5)如果减号右边的字符恰好是左边字符的后继,只删除中间的减号,例如:

“d-e”应输出为“de”,“3-4”应输出为“34”。

如果减号右边的字符按照ASCII码的顺序小于或等于左边字符,输出时,要保留中间的减号,例如:

“d-d”应输出为“d-d”,“3-1”应输出为“3-1”。

【输入】

   输入文件expand.in包括两行:

   第1行为用空格隔开的3个正整数,一次表示参数p1,p2,p3。

   第2行为一行字符串,仅由数字、小写字母和减号“-”组成。

行首和行末均无空格。

【输出】

    输出文件expand.out只有一行,为展开后的字符串。

【输入输出样例1】

expand.in

121

expand.out

abcs-w1234-9s-4zzabcsttuuvvw89s-4zz

【输入输出样例2】

expand.in

232

expand.out

a-d-daCCCBBBd-d

【输入输出样例3】

expand.in

342

expand.out

di-jkstra2-6dijkstra2************6

【限制】

   40%的数据满足:

字符串长度不超过5

   100%的数据满足:

1<=p1<=3,1<=p2<=8,1<=p3<=2。

字符串长度不超过100

解题思路:

也是个水题,考察对语言的运用能力,字符串处理的技巧和仔细读题的能力,测试数据已经包括所有情况了,但是有些细节要想清楚,而且一些特殊的情况也要单独测试一下,如9-a-a,--a-1;

需要注意的是:

首尾‘-’的情况,要用ansistring

Varp1,p2,p3,ii,jj,i,ch1,ch2:

longint;

s:

string;

procedureprint(st,en:

longint);

begin

  ifp3=1then

    forii:

=sttoendobegin

      forjj:

=1top2dowrite(chr(ii));

     end

   else

     forii:

=endowntostdobegin

       forjj:

=1top2dowrite(chr(ii));

     end;  

end;

procedureprint0(st,en:

longint);

begin

   forii:

=sttoendo

     forjj:

=1top2dowrite('*');

end;

procedurejudge;

begin

ch1:

=ord(s[i-1]);

ch2:

=ord(s[i+1]);

ifch1

  if(ch1>=48)and(ch1<=57)and(ch2>=48)and(ch2<=57)thenbegin

    ifp1=3thenprint0(ch1+1,ch2-1)elseprint(ch1+1,ch2-1);

  exit;

  end;

  if(ch1>=97)and(ch1<=122)and(ch2>=97)and(ch2<=122)thenbegin

    casep1of

      1:

print(ch1+1,ch2-1);

      2:

print(ch1-31,ch2-33);

      3:

print0(ch1+1,ch2-1);

   end;

   exit;

  end;

end;

write('-');

end;

begin

readln(p1,p2,p3);readln(s);write(s[1]);

fori:

=2tolength(s)-1do

   ifs[i]='-'thenjudge

elsewrite(s[i]);

writeln(s[length(s)]);

end.

                                          3.矩阵取数游戏(game.pas/c/cpp)

【问题描述】帅帅经常更同学玩一个矩阵取数游戏:

对于一个给定的n*m的矩阵,矩阵中的每个元素aij据为非负整数。

游戏规则如下:

   1.每次取数时须从每行各取走一个元素,共n个。

m次后取完矩阵所有的元素;

   2.每次取走的各个元素只能是该元素所在行的行首或行尾;

   3.每次取数都有一个得分值,为每行取数的得分之和;每行取数的得分=被取走的元素值*2i,其中i表示第i次取数(从1开始编号);

   4.游戏结束总得分为m次取数得分之和。

帅帅想请你帮忙写一个程序,对于任意矩阵,可以求出取数后的最大得分。

【输入】

   输入文件game.in包括n+1行;

   第一行为两个用空格隔开的整数n和m。

   第2~n+1行为n*m矩阵,其中每行有m个用单个空格隔开

【输出】

   输出文件game.out仅包含1行,为一个整数,即输入矩阵取数后的最大的分。

【输入输出样例1】

game.in

23

124

342

game.out

82

【输入输出样例1解释】

   第1次:

第一行取行首元素,第二行取行尾元素,本次的氛围1*21+2*21=6

   第2次:

两行均取行首元素,本次得分为2*22+3*22=20

   第3次:

得分为3*23+4*23=56。

总得分为6+20+56=82

【输入输出样例2】

game.in

14

4505

game.out

122

【输入输出样例3】

game.in

210

96565446861223888043

16951829305388836467

game.out

【限制】

   60%的数据满足:

1<=n,m<=30,答案不超过1016

   100%的数据满足:

1<=n,m<=80,0<=aij<=1000

解题思路:

很多人可能会被第一个样例误导认为贪心,第二个样例就提示我们贪心是错误的,其实有经验的选手一看就是dp,可以看作类似于石子归并,每次在2边进行合并我们可以把问题转化为每次要取完一整行,一共取m次。

注意到每一行都是独立的一个结构,所以可以每行的结果计算出来后求和。

   对于每一行,f(i,j)表示从i到j这一子段单独操作可以达到的最大权值。

   有状态转移方程:

f(i,j)=2*max(a(i)+f(i+1,j),a(j)+f(i,j-1))

   边界f(i,i)=2*a(i)

   最后答案就把每一行的f(0,m-1)加起来即可。

这里可以算得答案不超过30位10进制数,所以高精度的digit数组开到30足够了。

乘法也不用写,用两次加法即可。

Constmaxn=80;one=10000;

Typenum=array[0..25]oflongint;

Vara:

array[1..maxn]oflongint;f:

array[1..maxn,1..maxn]ofnum;

m,n:

longint;ans:

num;

procedureadd(varc:

num;a,b:

num);

vari,x:

longint;

begin

ifa[0]>b[0]thenc[0]:

=a[0]elsec[0]:

=b[0];x:

=0;

fori:

=1toc[0]do

begin

  x:

=a[i]+b[i]+x;

  c[i]:

=xmodone;

  x:

=xdivone;

end;

ifx>0thenbegininc(c[0]);c[c[0]]:

=x;end;

end;

proceduretime(vara:

num;k:

longint);

vari,x:

longint;

begin

x:

=0;

fori:

=1toa[0]dobeginx:

=a[i]*k+x;a[i]:

=xmodone;x:

=xdivone;end;

whilex>0dobegininc(a[0]);a[a[0]]:

=xmodone;x:

=xdivone;end;

end;

functioncompare(a,b:

num):

boolean;

vari:

longint;

begin

ifa[0]>b[0]thenexit(true);

ifb[0]>a[0]thenexit(false);

fori:

=a[0]downto1do

begin

  ifa[i]>b[i]thenexit(true);

  ifb[i]>a[i]thenexit(false);

end;

exit(false);

end;

procedureplus(varc:

num;a:

num;k:

longint);

vari,j:

longint;

begin

c:

=a;i:

=1;inc(c[i],k);

whilec[i]>=onedo

begin

  c[i+1]:

=c[i+1]+c[i]divone;

  c[i]:

=c[i]modone;

  inc(i);

  ifc[0]

=i;

end;

end;

proceduredp;

vari,j:

longint;max,temp:

num;

begin

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

fori:

=1tomdobeginf[i,i][0]:

=1;f[i,i][1]:

=a[i]*2;end;

fori:

=m-1downto1do

forj:

=i+1tomdo

  begin

   plus(max,f[i+1,j],a[i]);

   time(max,2);

   plus(temp,f[i,j-1],a[j]);

   time(temp,2);

   ifcompare(temp,max)thenmax:

=temp;

   f[i,j]:

=max;

  end;

add(ans,ans,f[1,m]);

end;

procedureinit;

vari,j:

longint;

begin

readln(n,m);

fori:

=1tondobeginforj:

=1tomdoread(a[j]);readln;dp;end;

end;

procedureouts;

vari:

longint;

begin

write(ans[ans[0]]);

fori:

=ans[0]-1downto1do

begin

  write(ans[i]div1000mod10);

  write(ans[i]div100mod10);

  write(ans[i]div10mod10);

  write(ans[i]mod10);

end;

end;

begin

init;outs;

end.

                                           4.树网的核(core.pas/c/cpp)

【问题描述】设T=(V,E,W)是一个无圈且连通的无向图(也称为无根树),每条边到有正整数的权,我们称T为树网(treebetwork),其中V,E分别表示结点与边的集合,W表示各边长度的集合,并设T有n个结点。

路径:

树网中任何两结点a,b都存在唯一的一条简单路径,用d(a,b)表示以a,b为端点的路径的长度,它是该路径上各边长度之和。

我们称d(a,b)为a,b两结点间的距离。

   D(v,P)=min{d(v,u),u为路径P上的结点}。

   树网的直径:

树网中最长的路径成为树网的直径。

对于给定的树网T,直径不一定是唯一的,但可以证明:

各直径的中点(不一定恰好是某个结点,可能在某条边的内部)是唯一的,我们称该点为树网的中心。

   偏心距ECC(F):

树网T中距路径F最远的结点到路径F的距离,即

          ECC(F)=max{d(v,F),v∈V}

   任务:

对于给定的树网T=(V,E,W)和非负整数s,求一个路径F,他是某直径上的一段路径(该路径两端均为树网中的结点),其长度不超过s(可以等于s),使偏心距ECC(F)最小。

我们称这个路径为树网T=(V,E,W)的核(Core)。

必要时,F可以退化为某个结点。

一般来说,在上述定义下,核不一定只有一个,但最小偏心距是唯一的。

   下面的图给出了树网的一个实例。

图中,A-B与A-C是两条直径,长度均为20。

点W是树网的中心,EF边的长度为5。

如果指定s=11,则树网的核为路径DEFG(也可以取为路径DEF),偏心距为8。

如果指定s=0(或s=1、s=2),则树网的核为结点F,偏心距为12。

【输入】

   输入文件core.in包含n行:

   第1行,两个正整数n和s,中间用一个空格隔开。

其中n为树网结点的个数,s为树网的核的长度的上界。

设结点编号以此为1,2,……,n。

   从第2行到第n行,每行给出3个用空格隔开的正整数,依次表示每一条边的两个端点编号和长度。

例如,“247”表示连接结点2与4的边的长度为7。

   所给的数据都是争取的,不必检验。

【输出】

    输出文件core.out只有一个非负整数,为指定意义下的最小偏心距。

【输入输出样例】

【输入输出样例1】

core.in

52

125

232

244

253

Core.out

5

【输入输出样例2】

core.in

86

132

232

346

453

464

472

783

core.out

5

【限制】

   40%的数据满足:

5<=n<=15

   70%的数据满足:

5<=n<=80

   100%的数据满足:

5<=n<=300,0<=s<=1000。

边长度为不超过1000的正整数

解题思路:

可以证明的是,如果图中存在多条路径,则在任何一条直径上都存在一条core(反证法,用到直径的距离最大性)。

因此只需讨论一条直径上的core的情况即可。

接着,如果一条路径包括了一条子路径的所有边,那么子路径的解不会比父路径更优。

这一点后面将用到。

    下面是算法:

先寻找一条直径:

任取一点u',遍历得到到它的最远点u,再对u寻找一个到它的最远点v,则路径u-v一定是一条直径(想想为什么),在刚才遍历u-v的时候记录遍历到每一个点的时候的前继,那么从v出发一直寻找前继到u,就能记录下这一条直径。

这里复杂度是O(n)。

   然后枚举core:

注意刚才说到了路径是越长越好,因此枚举的时候,每枚举一个初始点,向后都尽量延伸到不超过s的距离。

这样每个初始点只确定一个枚举对象。

对于终止点的选定可以采用一个游标的方式,当初始点从u到v扫过一遍时,游标也从u扫到了v(回想怎么求解凸多边形最远点对的),因此这里复杂度是O(n)。

   最后求ECC:

暂时删除枚举对象(那条路径)上的所有边,然后以那条路径上的那些顶点作为源点开始遍历图,找到的最大距离就是该路径的ECC。

这里复杂度也将是O(n)。

    因此总复杂度为O(n)+O(n)*O(n)=O(n^2),ac。

线性算法:

    同样只要考虑一条直径。

先对于直径上的顶点赋值:

与该顶点连通的最远点的距离。

这样可以构成一个线性模型,点(记为v[i])有一个值(记为f[i]),边有一个值(即原来图中v[i]和v[i+1]之间的距离,记为e[i])。

这一步可以O(n)完成。

   接着考虑这个线性模型。

枚举过程还是和刚才一样。

假设枚举的是v[a]到v[b]这一段路径,那么,这一段路径的ECC应当是以下几个值的最大值:

1)max(v[a],v[a+1]...,v[b]);2)max(v[i]+e[i]+e[i+1]+...+e[a-1],ib);

如果以上3个值能够在O

(1)内回答,那么总复杂度就将是O(n)

对于1,显然这是个RMQ模型,有一个O(n)算法(当然st的O(nlogn)也可以接受)。

或者考虑到这个问题的特殊性,所有(a,b)类的询问区间都不是严格包含的(即任取询问(a,b)(c,d)都不存在a>b&&c

   对于2,设一个数组a[1..n],a[k]表示max(v[i]+e[i]+...+e[k-1],i

   3和2同理,不再详述

Constmaxn=65536;

Varn,s,i,j,aa,bb,k,temp,st,en,ans,tou,max,len,dep,now:

longint;

w,f,mid:

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

p:

array[0..50000,1..2]ofinteger;

a:

array[0..300]ofinteger;

d:

array[0..300]oflongint;

begin

readln(n,s);

fori:

=1tondo

begin

     w[i,i]:

=0;f[i,i]:

=0;

     forj:

=i+1tondo

begin

       f[

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

当前位置:首页 > 外语学习 > 法语学习

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

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