NOIP提高组前三题解题报告.docx

上传人:b****4 文档编号:4904186 上传时间:2022-12-11 格式:DOCX 页数:14 大小:20.38KB
下载 相关 举报
NOIP提高组前三题解题报告.docx_第1页
第1页 / 共14页
NOIP提高组前三题解题报告.docx_第2页
第2页 / 共14页
NOIP提高组前三题解题报告.docx_第3页
第3页 / 共14页
NOIP提高组前三题解题报告.docx_第4页
第4页 / 共14页
NOIP提高组前三题解题报告.docx_第5页
第5页 / 共14页
点击查看更多>>
下载资源
资源描述

NOIP提高组前三题解题报告.docx

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

NOIP提高组前三题解题报告.docx

NOIP提高组前三题解题报告

NOIP2008提高组前三题解题报告

[日期:

2008-11-18]

来源:

 作者:

张恩权

[字体:

大中小]

NOIP2008提高组前三题解题报告

1.笨小猴

基本的字符串处理,细心一点应该没问题的,不过判断素数时似乎需要考虑下0和1的情况。

参考程序:

programword;

const

inp='word.in';

oup='word.out';

var

i,j,k,min,max:

longint;

s:

string;

ch:

char;

f:

array['a'..'z']ofinteger;//记录每个字符出现的次数

procedureflink;

begin

assign(input,inp);

reset(input);

assign(output,oup);

rewrite(output);

end;

procedurefclose;

begin

close(input);

close(output);

end;

functionjudge(k:

longint):

boolean;//判断素数,需考虑0和1的情况

var

i:

longint;

begin

if(k=0)or(k=1)thenexit(false);

fori:

=2totrunc(sqrt(k))do

ifkmodi=0thenexit(false);

exit(true);

end;

begin

flink;

readln(s);

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

fori:

=1tolength(s)do//统计每个字符出现的次数

inc(f[s[i]]);

min:

=1000;max:

=0;

forch:

='a'to'z'do//统计最大和最小

begin

if(f[ch]<>0)and(f[ch]>max)then

max:

=f[ch];

if(f[ch]<>0)and(f[ch]

min:

=f[ch];

end;

k:

=max-min;

ifjudge(k)then//输出

begin

writeln('LuckyWord');

write(k);

end

else

begin

writeln('NoAnswer');

write(0);

end;

fclose;

end.

2.火柴棒等式

预处理下,然后枚举、剪枝,范围稍微开大点,弄到2000似乎足够了,剪枝后不会超时的。

首先预处理下每个数(0~2000)需要多少个火柴棒,然后枚举A和B,再判断。

参考程序:

programmatches;

const

inp='matches.in';

oup='matches.out';

num:

array['0'..'9']ofinteger=(6,2,5,5,4,5,6,3,7,6);//0~9需要的火柴棒数

maxn=1000;

var

f:

array[0..maxn*2]oflongint;

i,j,k,n,ans:

longint;

s:

string;

procedureflink;

begin

assign(input,inp);

reset(input);

assign(output,oup);

rewrite(output);

end;

procedurefclose;

begin

close(input);

close(output);

end;

procedureinit;//预处理0~2000每个数需要的火柴棒数

var

i,j,k:

longint;

s:

string;

begin

fori:

=0tomaxn*2do

begin

str(i,s);

f[i]:

=0;

forj:

=1tolength(s)do

inc(f[i],num[s[j]]);

end;

end;

begin

flink;

readln(n);

init;

ans:

=0;

n:

=n-4;//总火柴棒数减去'+'和'='所需的火柴棒数

fori:

=0tomaxndo//枚举A

begin

iff[i]>=nthencontinue;//剪枝

forj:

=0tomaxndo//枚举B

begin

iff[i]+f[j]>=nthencontinue;//剪枝

k:

=i+j;

iff[i]+f[j]+f[k]=ntheninc(ans);//符合条件,总数加一

end;

end;

write(ans);

fclose;

end.

3.传纸条

从A传给B,再从B传给A,中间路径不相交与某个人……。

换种思路,它完全等价于,从A点传出2张纸条到B,中间不能经过同一人。

这么看来似乎就是vijos三取方格数的简化版了。

解题思想就是双进程的动态规划。

阶段是按传递次数划分的。

F(d,x1,y1,x2,y2)表示第d次传递到坐标为(x1,y1),(x2,y2)两个人手中是得到的最大好心度。

那么可以列出方程:

F(d,x1,y1,x2,y2)=max{f(d-1,x1',y1',x2',y2')+a[x1,y1]+a[x2,y2]|(x1',y1')为把纸条(x1,y1)的人,(x2',y2')为把纸条转给(x2,y2)的人}

(数学不太好,方程列的不专业,见谅^_^)

这么看来时间复杂度是O(maxd*n*m*n*m),其中maxd=n+m-2。

相当于50^5=312500000,超时是没得说的了。

其实我们可以把状态再压缩下,先来看下其中的规律:

起点(1,1)

第1次传递可到达(1,2)(2,1)

第2次传递可到达(1,3)(2,2)(3,1)

……

很快就可以发展其中的规律

第d次传递可到达的坐标(xi,yi)和d之间存在d+2=xi+yi

那么我们就可以把上面的状态转移方程转化成

f(d,x1,x2)=max{f(d-1,x1',x2')+a[x1,(d+2-x1)]+a[x2,(d+2-x2)]|x1',x2'合法}

参考程序:

programmessage;

const

inp='message.in';

oup='message.out';

var

a:

array[1..50,1..50]oflongint;

f:

array[0..200,1..100,1..100]oflongint;

tmp,i,j,k,n,m,d,x1,x2,y1,y2:

longint;

procedureflink;

begin

assign(input,inp);

reset(input);

assign(output,oup);

rewrite(output);

end;

procedurefclose;

begin

close(input);

close(output);

end;

functionmax(a,b:

longint):

longint;

begin

ifa>bthenexit(a);

exit(b);

end;

begin

flink;

readln(n,m);

fori:

=1tondo

begin

forj:

=1tomdo

read(a[i,j]);

readln;

end;

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

f[0,1,1]:

=a[1,1];

f[1,1,2]:

=a[2,1]+a[1,2];f[1,2,1]:

=a[1,2]+a[2,1];

//边界,因为中间需要判断点不重合,所以把必须重合的状态单独考虑

ford:

=2to(n+m-3)do

forx1:

=1tondo

begin

y1:

=(d+2)-x1;

if(y1<=0)or(y1>m)thencontinue;//排除不合法的x1

forx2:

=1tondo

begin

ifx1=x2thencontinue;//排除不合法的x1,x2

y2:

=(d+2)-x2;

if(y2<=0)or(y2>m)thencontinue;//排除不合法的x2

tmp:

=f[d-1,x1,x2];

//各自从上方取,即从(x1,y1-1)传到(x1,y1),(x2,y2-1)传到(x2,y2)

if(x1-1>0)and(x2-1>0)then

tmp:

=max(tmp,f[d-1,x1-1,x2-1]);

//从(x1-1,y1)传到(x1,y1),(x2-1,y2)传到(x2,y2)

if(x1-1>0)and(x1-1<>x2)then

tmp:

=max(tmp,f[d-1,x1-1,x2]);

//从(x1-1,y1)传到(x1,y1),(x2,y2-1)传到(x2,y2)

if(x2-1>0)and(x1<>x2-1)then

tmp:

=max(tmp,f[d-1,x1,x2-1]);

//从(x1,y1-1)传到(x1,y1),(x2-1,y2)传到(x2,y2)

f[d,x1,x2]:

=tmp+a[x1,y1]+a[x2,y2];

end;

end;

write(f[n+m-3,n,n-1]);//终点的只可能从两个点来,所以终点状态前移一个阶段。

fclose;

end.

 

4.双栈排序

难……,写不出满分程序,所以还是不写了……

 

NOIP2008提高组复赛解题思路

1、字符串中统计字母出现次数最大减最小的然后判断质数字符串长度<=100

2、给出n<=24个火柴棍,求最多能摆出多少a+b=c形式的等式。

数字的摆法和计算器相同,a,b,c>=0

3、双进程方格取数:

m*n的棋盘,m,n<=50,不需使用高精度

4、给出1..n的排列,两个栈S1、S2,入栈出栈共4个操作:

(n<=1000)

   A:

输入队列头入S1

   B:

弹出S1栈顶元素,进入输出队列

   C:

输入队列头入S2

   D:

弹出S2栈顶元素,进入输出队列

   要求将1..n的排列排序,采用字典序最小的操作方法

个人感觉是,单就难度来看,奥赛历史最简单,由于类似2,3题的模型大多数能拿一等的同学们都见过。

我的想法:

1、水题,最多40分钟搞定

2、如果是考试的话,10000*10000的枚举,差不多写程序+跑+打表=10+3+2min就够了吧(不过我用的是骗

分手段,一个一个手算。

然后IF-THEN,IF-THEN,最后15分钟搞定)。

3、经典题目,斜向划分阶段。

35分钟搞定(前三个题目1小时思路+编程+检查够了)

4、本届唯一挑战性题目。

考虑到要求字典序最小,那么按照ABCD的顺序贪心即可。

对于当前的状态,设该进入到输出序列中的是p,输入队列头是q,S1栈顶是t1,S2栈顶是t2,那么依次判

断,容易知道q>=p

i)如果q

ii)如果t1=p,执行B,continue;

iii)如果q

iv)如果q=t2,执行D,continue;

v)输出无解信息,halt;

这个算法应该是错误的。

一个反例是:

7251463。

在上述四个判断条件中,能够产生冲突的只有i和iii,在i和iii冲突的时候需要判断。

所以我对上述算法进行修改:

i)如果((q

ii)如果t1=p,执行B,continue;

iii)如果q

iv)如果q=t2,执行D,continue;

v)输出无解信息,halt;

归纳法证明算法正确性:

当n很小的时候(至少我没举出反例)算法是正确的。

设n

当q=k时,显然(不存在x,使得q

不存在或者t2不存在。

只要最大的数k放在了栈底,对其他的数都是没有影响的。

所以算法依然正确。

证毕。

(表述的很不规范。

 

NOIP2008提高组解题报告

angwuy

1word

这道题完全是送分题,只需要直接统计,再判断素数。

参考程序:

var

st:

string;

max,min,i:

longint;

a:

array['a'..'z']oflongint;

ch:

char;

functionfun(n:

longint):

boolean;

vari:

longint;

begin

ifn<2thenbeginfun:

=false;exit;end;

fori:

=2ton-1do

ifnmodi=0thenbeginfun:

=false;exit;end;

fun:

=true;

end;

begin

assign(input,'word.in');

reset(input);

assign(output,'word.out');

rewrite(output);

readln(st);

fillchar(a,sizeof(a),0);

fori:

=1tolength(st)do

inc(a[st[i]]);

max:

=0;

min:

=101;

forch:

='a'to'z'do

ifa[ch]>0then

begin

ifa[ch]>maxthenmax:

=a[ch];

ifa[ch]

=a[ch];

end;

iffun(max-min)then

begin

writeln('LuckyWord');

writeln(max-min);

end

else

begin

writeln('NoAnswer');

writeln(0);

end;

close(input);

close(Output);

end.

2matches

搜索题,由于输入的情况只有25种,所以打表也是一种可行的方法。

在数据最大时,经过人工和电脑证明是不会到达四位数的,所以可以直接用O(1000*1000)的搜索算法

参考程序:

const

mat:

array[0..9]oflongint=(6,2,5,5,4,5,6,3,7,6);

functionfun(m:

longint):

longint;

vart:

longint;

begin

t:

=0;

whilem>0do

begin

inc(t,mat[mmod10]);

m:

=mdiv10;

end;

fun:

=t;

end;

vara:

array[0..1000]oflongint;

n,i,j,ans:

longint;

begin

assign(input,'matches.in');

reset(input);

assign(output,'matches.out');

rewrite(output);

readln(n);

ifn<10thenbeginwriteln(0);close(output);exit;end;

a[0]:

=6;

fori:

=1to1000do

a[i]:

=fun(i);

dec(n,4);

fori:

=0to1000do

ifa[i]

begin

forj:

=0to1000-ido

ifa[i]+a[j]+a[i+j]=ntheninc(ans);

end;

writeln(ans);

close(input);

close(output);

end.

3message

DP题,两条路线必定一上一下,而且,当到达某一列后,前面对后面的不会有影响,符合动态规划的无后效性,方程如下:

用dp[I,j,k]表示当到达I列时,上路线在j行到,下路线在k行到的最大值。

另外加一个预处理,sum[I,j1,j2]表示在第I列j1到j2行的数加起来的和。

边界条件:

dp[2,1,k]:

=sum[1,1,k];

递推方程:

dp[I,j,k]:

=max(dp[I-1,j2,k2]+sum[I-1,j,j2]+sum[I-1,k,k2]){j<=j2

答案:

max(dp[m,j,n]+sum[m,j,n])

参考程序:

constmaxn=10;

var

a:

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

dp,sum:

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

n,m,i,j,k,i1,i2,j1,j2,k1,k2:

longint;

functionmax(a,b:

longint):

longint;

begin

ifa>bthenmax:

=aelsemax:

=b;

end;

begin

assign(input,'message.in');

reset(input);

assign(output,'message.out');

rewrite(output);

readln(m,n);

fori:

=1tomdo

begin

forj:

=1tondo

read(a[i,j]);

readln;

end;

fori:

=1tomdo

begin

fori1:

=1tondo

begin

sum[i,i1,i1]:

=a[i,i1];

fori2:

=i1+1tondo

sum[i,i1,i2]:

=sum[i,i1,i2-1]+a[i,i2];

end;

end;

fillchar(dp,sizeof(dp),255);

fori:

=2tondo

dp[2,1,i]:

=sum[1,1,i];

fori:

=2tom-1do

forj:

=1ton-1do

fork:

=j+1tondo

ifdp[i,j,k]>-1then

begin

forj2:

=jtok-1do

fork2:

=ktondo

dp[i+1,j2,k2]:

=max(dp[i+1,j2,k2],dp[i,j,k]+sum[i,j,j2]+sum[i,k,k2]);

end;

k:

=0;

fori:

=1ton-1do

k:

=max(k,dp[m,i,n]+sum[m,i,n]);

writeln(k);

close(input);

close(output);

end.

第四题的解题报告都有很多大牛写了,那我就不在这里献丑了

好象是用二分图来做的,在OIBH上有详细的题解

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

当前位置:首页 > 求职职场 > 简历

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

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