第九届全国青少年信息学奥林匹克联赛复赛.docx

上传人:b****3 文档编号:3799310 上传时间:2022-11-25 格式:DOCX 页数:20 大小:25.42KB
下载 相关 举报
第九届全国青少年信息学奥林匹克联赛复赛.docx_第1页
第1页 / 共20页
第九届全国青少年信息学奥林匹克联赛复赛.docx_第2页
第2页 / 共20页
第九届全国青少年信息学奥林匹克联赛复赛.docx_第3页
第3页 / 共20页
第九届全国青少年信息学奥林匹克联赛复赛.docx_第4页
第4页 / 共20页
第九届全国青少年信息学奥林匹克联赛复赛.docx_第5页
第5页 / 共20页
点击查看更多>>
下载资源
资源描述

第九届全国青少年信息学奥林匹克联赛复赛.docx

《第九届全国青少年信息学奥林匹克联赛复赛.docx》由会员分享,可在线阅读,更多相关《第九届全国青少年信息学奥林匹克联赛复赛.docx(20页珍藏版)》请在冰豆网上搜索。

第九届全国青少年信息学奥林匹克联赛复赛.docx

第九届全国青少年信息学奥林匹克联赛复赛

第九届全国青少年信息学奥林匹克联赛(N0IP2003)

复赛提高组解题报告

题一源程序题二源程序题三源程序题四源程序

题一神经网络

【问题背景】

人工神经网络(ArtificialNeuralNetwork)是一种新兴的具有自我学习能力的计算系统,在模式识别、函数逼近及贷款风险评估等诸多领域有广泛的应用。

对神经网络的研究一直是当今的热门方向,兰兰同学在自学了一本神经网络的入门书籍后,提出了一个简化模型,他希望你能帮助他用程序检验这个神经网络模型的实用性。

【问题描述】

在兰兰的模型中,神经网络就是一张有向图,图中的节点称为神经元,而且两个神经

元之间至多有一条边相连,下图是一个神经元的例子:

神经元〔编号为1)

图中,X1—X3是信息输入渠道,Y1-Y2是信息输出渠道,C1表示神经元目前的状态,

Ui是阈值,可视为神经元的一个内在参数。

神经元按一定的顺序排列,构成整个神经网络。

在兰兰的模型之中,神经网络中的神

经无分为几层;称为输入层、输出层,和若干个中间层。

每层神经元只向下一层的神经元

输出信息,只从上一层神经元接受信息。

下图是一个简单的三层神经网络的例子。

兰兰规定,Ci服从公式:

(其中n是网络中所有神经元的数目)

公式中的Wji(可能为负值)表示连接j号神经元和i号神经元的边的权值。

当Ci大于0时,该神经元处于兴奋状态,否则就处于平静状态。

当神经元处于兴奋状态时,下一秒它会向其他神经元传送信号,信号的强度为Ci。

如此.在输入层神经元被激发之后,整个网络系统就在信息传输的推动下进行运作。

现在,给定一个神经网络,及当前输入层神经元的状态(Ci),要求你的程序运算出最后网络输出层的状态。

【输入格式】

输入文件第一行是两个整数n(1≤n≤200)和p。

接下来n行,每行两个整数,第i+1行是神经元i最初状态和其阈值(Ui),非输入层的神经元开始时状态必然为0。

再下面P行,每行由两个整数i,j及一个整数Wij,表示连接神经元i、j的边权值为Wij。

【输出格式】

输出文件包含若干行,每行有两个整数,分别对应一个神经元的编号,及其最后的状态,两个整数间以空格分隔。

仅输出最后状态非零的输出层神经元状态,并且按照编号由小到大顺序输出!

若输出层的神经元最后状态均为0,则输出NULL。

【输入样例】

56

10

10

01

01

01

131

141

151

231

241

251

【输出样例】

31

41

51

[分析]本题是比较简单的,但要注意神经元的层数,只输出最大层(输出层)的状态非零的神经元的状态,在中间层中只有神经元处于兴奋状态时(Ci>0)才会向下一层传送信号。

[PASCAL源程序]

programNOIP2003_1_Network;

const

maxn=200;maxp=200;

var

i,j,n,p,maxlayer:

integer;

w:

array[0..maxp]oflongint;{存放边的权值}

start,terminal:

array[0..maxp]ofbyte;{存放边的起点与终点}

u,c:

array[0..maxn]oflongint;{存放神经元的阀值与状态值}

layer:

array[0..maxn]ofbyte;{存放神经元的层数}

f1,f2:

text;fn1,fn2,fileNo:

string;

flag:

boolean;

begin

write('InputfileNo:

');

readln(fileNo);

fn1:

='network.in'+fileNo;

fn2:

='network.ou'+fileNo;

assign(f1,fn1);reset(f1);

assign(f2,fn2);rewrite(f2);

readln(f1,n,p);

fori:

=1tondoreadln(f1,c[i],u[i]);

fillchar(layer,sizeof(layer),0);

fori:

=1topdobegin

readln(f1,start[i],terminal[i],w[i]);{读入边的起点,终点,权}

layer[terminal[i]]:

=layer[start[i]]+1;{计算终点的层数(比起点大1)}

end;

close(f1);

maxlayer:

=layer[terminal[p]];{求最大层数,即输出层的层数}

fori:

=1tondo{计算非输入层的节点i的状态值}

iflayer[i]>0thenbegin

forj:

=1topdo

if(terminal[j]=i)and(c[start[j]]>0){与目标节点i有边相连的节点j且其状态处于兴奋时(Cj>0)才向节点I传送信号}

thenc[i]:

=c[i]+w[j]*c[start[j]];

c[i]:

=c[i]-u[i];

end;

{输出结果}

flag:

=true;

fori:

=1tondo

if(layer[i]=maxlayer)and(c[i]>0)thenbegin

writeln(f2,i,'',c[i]);

flag:

=false;

end;

ifflagthenwriteln(f2,'NULL');

close(f2);

end.

[点评]基本题。

题目阅读起来有些费神,题中边的权值W,神经元的状态值C,阀值U,均未明确指出其取值范围,反映出命题不严谨。

题二侦探推理

【问题描述】

明明同学最近迷上了侦探漫画《柯南》并沉醉于推理游戏之中,于是他召集了一群同学玩推理游戏。

游戏的内容是这样的,明明的同学们先商量好由其中的一个人充当罪犯(在明明不知情的情况下),明明的任务就是找出这个罪犯。

接着,明明逐个询问每一个同学,被询问者可能会说:

证词中出现的其他话,都不列入逻辑推理的内容。

明明所知道的是,他的同学中有N个人始终说假话,其余的人始终说真。

现在,明明需要你帮助他从他同学的话中推断出谁是真正的凶手,请记住,凶手只有一个!

【输入格式】

输入由若干行组成,第一行有二个整数,M(1≤M≤20)、N(1≤N≤M)和P(1≤P≤100);M是参加游戏的明明的同学数,N是其中始终说谎的人数,P是证言的总数。

接下来M行,每行是明明的一个同学的名字(英文字母组成,没有主格,全部大写)。

往后有P行,每行开始是某个同学的名宇,紧跟着一个冒号和一个空格,后面是一句证词,符合前表中所列格式。

证词每行不会超过250个字符。

输入中不会出现连续的两个空格,而且每行开头和结尾也没有空格。

【输出格式】

如果你的程序能确定谁是罪犯,则输出他的名字;如果程序判断出不止一个人可能是罪犯,则输出CannotDetermine;如果程序判断出没有人可能成为罪犯,则输出Impossible。

【输入样例】

315

MIKE

CHARLES

KATE

MIKE:

Iamguilty.

MIKE:

TodayisSunday.

CHARLES:

MIKEisguilty.

KATE:

Iamguilty.

KATE:

Howareyou?

?

【输出样例】

MIKE

[分析]基本思路有两种:

一是可以穷举M个人中有N个人始终说假话的所有组合,据此出发,判断每句证词的真伪,再推断谁是罪犯,但这样做运算量大;二是可以穷举M个人中的任意一个是罪犯,由此再来判断每句证词的真伪,推断谁说真话谁说假话,这样做运算量小得多。

这里采用后者。

有几点必须注意:

一,不能说找到某人是罪犯或可能是罪犯就完事了,还必须确保是否还有别人是罪犯或可能是罪犯;二,如何处理关于星期的证词呢?

还是可以采用穷举;三,某人的证词可能前后矛盾,在给某人标记他是始终说真话还是始终说假话时,一定要考察他此前的诚信记录;因此,对某人的诚信记录要有4种不同的区分,可用0表示此人尚无有效记录(未说过真话也未说过假话),用1表示此人说真话,用2表示此人说假话,用3表示此人说的话与他前面的证词冲突;四,如何判断最初穷举时设定的前提(某人是罪犯)是否是本题的一个解呢?

如果有人的诚信记录为3,则肯定不是本题的解;但是也不能强求诚信记录为2的人的总数一定要等于n,而是只要诚信记录为2的人不超过n且诚信记录为1的人不超过m-n即可,因为诚信记录为0的人可能说真话也可能说假话,他们只是没有说话,或只说了废话。

五,由于证词要反复用于判断,可以先剔除其中的无效证词;为处理方便,将有效证词分为两部分:

不含星期的和含星期的。

[PASCAL源程序]

programNOIP2003_2_Logic;

const

maxm=20;

dow:

array[1..7]ofstring=('Sunday.','Monday.','Tuesday.','Wednesday.',

'Thursday.','Friday.','Saturday.');

var

i,j,k,weekday,m,n,p,p1,p2,p3,index,resolution,total1,total2:

byte;

name:

array[1..maxm]ofstring;{存放人名}

witness10,witness20:

array[1..100]ofbyte;{存放说话人的序号,分为两类,前者存放不含星期的证词的说话人的序号,后者存放只含星期的证词的说话人的序号}

witness1,witness2:

array[1..100]ofstring;{存放证词,分为两类,第一类是不含星期的证词,第二类是只含星期的证词}

name0,temp,temp0,temp1,temp2:

string;

truth,truth0:

array[1..maxm]ofbyte;{存放诚信记录}

f1,f2:

text;fn1,fn2,fileNo:

string;

flag:

boolean;

begin

write('InputfileNo:

');readln(fileNo);

fn1:

='logic.in'+fileNo;fn2:

='logic.ou'+fileNo;

assign(f1,fn1);reset(f1);assign(f2,fn2);rewrite(f2);

readln(f1,m,n,p);

fori:

=1tomdoreadln(f1,name[i]);

total1:

=0;total2:

=0;

fori:

=1topdobegin{对证词预处理}

readln(f1,temp);

index:

=pos(':

',temp);

temp1:

=copy(temp,1,index-1);{取得说话人姓名}

temp2:

=copy(temp,index+2,length(temp)-index-1);{取得证词}

if(temp2='Iamguilty.')or(temp2='Iamnotguilty.')then

forj:

=1tomdo

ifname[j]=temp1thenbegin

inc(total1);{total1表示第一类证词的总数}

witness10[total1]:

=j;{记下第一类第total1条证词的说话人的序号}

witness1[total1]:

=temp2;{记下第一类第total1条证词}

break;

end;

if(pos('isguilty.',temp2)>0)or(pos('isnotguilty.',temp2)>0)thenbegin

temp0:

=copy(temp2,1,pos('is',temp2)-1);{取得证词的叙述对象(主语)}

flag:

=false;

fork:

=1tomdo

iftemp0=name[k]thenbegin

flag:

=true;

break;

end;

ifflagthen{如果证词的叙述对象(主语)确实存在}

forj:

=1tomdo

ifname[j]=temp1thenbegin{记入到第一类证词中}

inc(total1);

witness10[total1]:

=j;

witness1[total1]:

=temp2;

break;

end;

end;

flag:

=false;

forj:

=1to7do

iftemp2='Todayis'+dow[j]thenbegin

flag:

=true;

break;

end;

ifflagthen{如果证词是关于星期的判断}

forj:

=1tomdo

ifname[j]=temp1thenbegin{记入到第二类证词中}

inc(total2);{total2表示第二类证词的总数}

witness20[total2]:

=j;{记下第二类第total2条证词的说话人的序号}

witness2[total2]:

=temp2;{记下第二类第total2条证词}

break;

end;

end;

close(f1);

resolution:

=0;{resolution表示解的个数}

fori:

=1tomdobegin{穷举,第i个人为罪犯}

ifresolution>1thenbreak;{如果解的个数多于1个,则跳出循环}

fillchar(truth,sizeof(truth),0);{诚信记录赋初值为0,表示此人尚无有效证词}

forj:

=1tototal1dobegin{逐条处理第一类证词}

ifwitness1[j]='Iamguilty.'thenbegin{如果证词为Iamguilty.}

ifi=witness10[j]then{如果说话人就是罪犯,则本证词为真}

casetruth[i]of

0:

truth[i]:

=1;{如果此人的诚信记录为0,则此人说真话(记为1)}

2:

truth[i]:

=3;{如果此人的诚信记录为2(即以前说假话),则此人自相矛盾(记为3)}

end

else{如果说话人不是罪犯,则本证词为假}

casetruth[witness10[j]]of

0:

truth[witness10[j]]:

=2;{如果此人的诚信记录为0,则此人说假话(记为2)}

1:

truth[witness10[j]]:

=3;{如果此人的诚信记录为1(即以前说真话),则此人自相矛盾(记为3)}

end;

end;

ifwitness1[j]='Iamnotguilty.'thenbegin{如果证词为Iamnotguilty.}

ifi=witness10[j]then{如果说话人是罪犯,则本证词为假}

casetruth[i]of

0:

truth[i]:

=2;

1:

truth[i]:

=3;

end

else{如果说话人不是罪犯,则本证词为真}

casetruth[witness10[j]]of

0:

truth[witness10[j]]:

=1;

2:

truth[witness10[j]]:

=3;

end;

end;

if(pos('isguilty.',witness1[j])>0)thenbegin{如果证词含有isguilty.}

temp:

=copy(witness1[j],1,pos('isguilty.',witness1[j])-1);{取得证词的主语}

ifname[i]=tempthen{如果证词的主语是罪犯,则本证词为真}

casetruth[witness10[j]]of

0:

truth[witness10[j]]:

=1;

2:

truth[witness10[j]]:

=3;

end

else{如果证词的主语不是罪犯,则本证词为假}

casetruth[witness10[j]]of

0:

truth[witness10[j]]:

=2;

1:

truth[witness10[j]]:

=3;

end;

end;

if(pos('isnotguilty.',witness1[j])>0)thenbegin{如果证词含有isnotguilty.}

temp:

=copy(witness1[j],1,pos('isnotguilty.',witness1[j])-1);{取得证词的主语}

ifname[i]=tempthen{如果证词的主语是罪犯,则本证词为假}

casetruth[witness10[j]]of

0:

truth[witness10[j]]:

=2;

1:

truth[witness10[j]]:

=3;

end

else{如果证词的主语不是罪犯,则本证词为真}

casetruth[witness10[j]]of

0:

truth[witness10[j]]:

=1;

2:

truth[witness10[j]]:

=3;

end;

end;

end;{第一类证词全部处理完毕}

iftotal2>0thenbegin{如果有第二类证词存在,处理第二类证词}

fork:

=1tomdotruth0[k]:

=truth[k];{记下第一类证词全部处理完毕后的诚信记录}

forweekday:

=1to7dobegin{穷举,今天是星期日,星期一直到星期六}

fork:

=1tomdotruth[k]:

=truth0[k];{诚信记录还原为第一类证词全部处理完毕后的诚信记录}

forj:

=1tototal2do{逐条处理第二类证词}

ifpos(dow[weekday],witness2[j])>0then{如果证词中含有当前穷举的星期值,则本证词为真}

casetruth[witness20[j]]of

0:

truth[witness20[j]]:

=1;

2:

truth[witness20[j]]:

=3;

end

else{如果证词中不含有当前穷举的星期值,则本证词为假}

casetruth[witness20[j]]of

0:

truth[witness20[j]]:

=2;

1:

truth[witness20[j]]:

=3;

end;

p1:

=0;p2:

=0;p3:

=0;

fork:

=1tomdoiftruth[k]=1theninc(p1);{p1表示始终说真话的人的总数}

fork:

=1tomdoiftruth[k]=2theninc(p2);{p2表示始终说假话的人的总数}

fork:

=1tomdoiftruth[k]=3theninc(p3);{p3表示说过自相矛盾的话的人的总数}

if(p1<=m-n)and(p2<=n)and(p3=0)thenbegin{如果说真话的人的总数小于等于m-n且说假话的人的总数小于等于n且没有人说过自相矛盾的话,那么当前罪犯i就是本题的一个解}

name0:

=name[i];{记下罪犯的姓名}

inc(resolution);{解的个数增1}

break;{退出星期的穷举}

end;

end;{星期的穷举完毕}

end;{第二类证词处理完毕}

p1:

=0;p2:

=0;p3:

=0;

fork:

=1tomdoiftruth[k]=1theninc(p1);

fork:

=1tomdoiftruth[k]=2theninc(p2);

fork:

=1tomdoiftruth[k]=3theninc(p3);

if(p1<=m-n)and(p2<=n)and(p3=0)and(name0<>name[i])thenbegin{为避免重复计解,此处多加了一个条件:

name0<>name[i]}

name0:

=name[i];

inc(resolution);

end;

end;

ifresolution=1thenwriteln(f2,name0);{如果只有一个解,则输出罪犯姓名}

ifresolution=0thenwriteln(f2,'Impossible');{如果无解,则输出Impossible}

ifresolution>1thenwriteln(f2,'CannotDetermine');{如果有多个可能解,则输出CannotDetermine}

close(f2);

end.

[点评]基本题,比较复杂,重点考查参赛者的字符串运算和逻辑运算,逻辑推理能力。

难点主要在于如何处理关于星期的证词,以及证词之间是否存在矛盾。

题三加分二叉树

【问题描述】

设一个n个节点的二叉树tree的中序遍历为(l,2,3,…,n),其中数字1,2,3,…,n为节点编号。

每个节点都有一个分数(均为正整数),记第i个节点的分数为di,tree及它的每个子树都有一个加分,任一棵子树subtree(也包含tree本身)的加分计算方法如下:

subtree的左子树的加分×subtree的右子树的加分+subtree的根的分数

若某个子树为空,规定其加分为1,叶子的加分就是叶节点本身的分数。

不考虑它的空子树。

试求一棵符合中序遍历为(1,2,3,…,n)且加分最高的二叉树tree。

要求输出;

(1)tree的最高加分

(2)tree的前序遍历

【输入格式】

第1行:

一个整数n(n<30),为节点个数。

第2行:

n个用空格隔开的整数,为每个节点的分数(分数<100)。

【输出格式】

第1行:

一个整数,为最高加分(结果不会超过4,000,000,000)。

第2行:

n个用空格隔开的整数,为该树的前序遍历。

【输入样例】

5

571210

【输出样例】

145

31245

[分析]很显然,本题适合用动态规划来解。

如果用数组value[i,j]表示从节

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

当前位置:首页 > PPT模板 > 可爱清新

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

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