收藏 分销(赏)

NoiP2003提高组复赛试题分析_天津南开中学滕伟.doc

上传人:xrp****65 文档编号:7228013 上传时间:2024-12-28 格式:DOC 页数:15 大小:64KB 下载积分:10 金币
下载 相关 举报
NoiP2003提高组复赛试题分析_天津南开中学滕伟.doc_第1页
第1页 / 共15页
NoiP2003提高组复赛试题分析_天津南开中学滕伟.doc_第2页
第2页 / 共15页


点击查看更多>>
资源描述
我看了李学武教授写的《关于NOIP复赛的若干问题的思考》一文,有很大触动,深刻感受到天津市信息学奥赛基础薄弱。我从事奥赛培训工作十三年,培训的学生在全国取得了一些好成绩,一块国际银牌,四块全国奥赛金牌,三块银牌,十块铜牌。我认为我有责任将我的培训工作介绍给同人和其他学校的同学,来推动天津市奥赛的发展。下面是我写的今年分区联赛高中组复赛的解题分析,供大家参考,并恳切希望能与“OIER”交流共勉。 天津南开中学滕伟( 邮编:300100,电子邮箱:fnoi2003@ 或 tjnkzx2000@ ) 第一题:神经网络 【试题分析】 一、题意分析 1、任务描述:从输入层开始,各节点按照传递公式,一层一层向下传递。输出输出层中信号大于零的节点编号和信号大小。(节点编号由小到大)如果没有满足条件的编号则输出NULL。 信号传递公式: 公式中的Wji(可能为负值)表示连接j号神经元和i号神经元的边的权值。当Ci大于0时,该神经元处于兴奋状态,否则就处于平静状态。当神经元处于兴奋状态时,下一秒它会向其它神经元传递信号,信号的强度为Ci。 2、输入: 两个整数n(1≤n≤20)和p。n表示节点的个数;p表示有向边的条数。 下面n行表示1-n号节点的状态和阈值。 下面p行表示有向边及其权值。 3、输出: 输出输出层状态大于零的神经元编号和状态,并且按照编号有小到大顺序输出! 若输出层的神经元最后状态小于等于0,则输出NULL。 二、问题分析 1、题目中给出每层神经元只向下一层的神经元输出信息,只从上一层神经元接受信息。所以不必进行拓扑排序,一层一层的向下传递信号即可。输出最后一层中信号大于零的节点编号。 2、可以建立一个队列,将输入层节点入队。 3、取队首节点出队,寻找此节点有向边,如果有有向边: 1)则记录此节点不是输出层; 2)再判断此节点信号大于零则向下传递信号,将指向的节点入队(防止重复入队)。再出队再传递,直至全部出队。 注意:1)输入层可以是输出层。2)信号传递公式中只减一次U[i]。 【程序清单】 下面是我校学生肖天同学写的程序。 {NOIP 2003 Problem 1 by Xiao Tian} {Test 1..5 : All right} Program network; Const InName='network.in'; OutName='network.out'; MM=100; Var InFile,OutFile:Text; C,U:Array[1..MM] Of LongInt; Map:Array[1..MM,1..MM] Of LongInt; Flag:Array[1..MM,1..MM] Of Boolean; IsOut:Array[1..MM] Of Boolean; Queue:Array[1..MM] Of LongInt; N,P,i,Int1,Int2,Head,Rear:LongInt; IsInQueue:Array[1..MM] Of Boolean; IsNull:Boolean; Begin Assign(InFile,InName); Reset(InFile); ReadLn(InFile,N,P); For i:=1 To N Do ReadLn(InFile,C[i],U[i]); FillChar(Flag,SizeOf(Flag),False); For i:=1 To P Do Begin Read(InFile,Int1,Int2); ReadLn(InFile,Map[Int1,Int2]); Flag[Int1,Int2]:=True; End; Close(InFile); FillChar(IsOut,SizeOf(IsOut),True); FillChar(IsInQueue,SizeOf(IsInQueue),False); Head:=1; Rear:=1; For i:=1 To N Do Begin If C[i]>0 Then Begin Queue[Rear]:=i; Inc(Rear); IsInQueue[i]:=True; End Else C[i]:=-U[i]; End; While Head<Rear Do Begin For i:=1 To N Do If Flag[Queue[Head],i] Then Begin If C[Queue[Head]]>0 Then Begin Inc(C[i],Map[Queue[Head],i]*C[Queue[Head]]); If Not IsInQueue[i] Then Begin Queue[Rear]:=i; Inc(Rear); IsInQueue[i]:=True; End; End; IsOut[Queue[Head]]:=False; End; Inc(Head); End; Assign(OutFile,OutName); Rewrite(OutFile); IsNull:=True; For i:=1 To N Do If IsOut[i] Then If C[i]>0 Then Begin WriteLn(OutFile,i,' ',C[i]); IsNull:=False; End; If IsNull Then WriteLn(OutFile,'NULL'); Close(OutFile); End. 第二题:侦探推理 【试题分析】 一、题意分析 1、任务描述:M个人参加游戏,每人提供一句或多句证言,共P句证言。其中N个人始终说假话,其余的人始终说真话。请你通过证言判断出谁是罪犯。 证言形式如下: 证词内容 证词含义 I am guilty. 我是罪犯 I am not guilty. 我不是罪犯 XXX is guilty. XXX是罪犯(XXX表示某个同学的名字) XXX is not guilty. XXX不是罪犯 Today is XXX. 今天是XXX(XXX表示星期几,是Monday Tuesday Wednesday Thursday Friday Saturday Sunday其中之一) 证词中出现的其它话,都不列入逻辑推理的内容。 2、输入: 第一行有三个整数,M(1≤M≤20)、N(1≤N≤M)和P(1≤P≤100):M是参加游戏的明明的同学数,N是其中始终说谎的人数,P是证人的综述。 接下来M行,每行是明明的一个同学的名字(英文字母组成,没有空格,全部大写)。 往后有P行,每行开始是某个同学的名字,紧跟着一个冒号和一个空格,后面是一句证词,符合前表中所列格式,证词每行不会好过250个字符。 输入中不会出现连续的两个空格,而且每行开头和结尾也没有空格。 3、输出: 如果你的程序能确定谁是罪犯,则输出他的名字; 如果程序判断出不止一个人可能是罪犯,则输出Cannot Determine; 如果程序判断出没有人可能成为罪犯,则输出Impossible。 二、问题分析 1、根据题意可知可以利用枚举法完成。枚举罪犯和今天是星期几。满足N个人始终说谎话和M-N个人始终说真话的条件,就可以确定罪犯。 2、首先利用字符串操作将证言转化成计算机可表示的信息。 1)定义Guilty:Array[1..MM,1..MM] Of Integer;{-1..1} FillChar(Guilty,SizeOf(Guilty),0) 初值为0 Guilty[i,j]=-1:表示第i个人说第j个人不是罪犯 Guilty[i,j]= 1:表示第i个人说第j个人是罪犯 其中包含Guilty[i,i]即第i个人说自己是不是罪犯 注意:Guilty不可以自相矛盾。 2)定义WhatDay:Array[1..MM,1..7] Of Boolean; FillChar(WhatDay,SizeOf(WhatDay),False) WhatDay[i,j]:=True :表示第i个人说过j是星期几 3、枚举罪犯和星期几,判断每句证言是真是假,统计说真假证言的人数。注意:有可能某人又说过真话,又说过假话。 4、根据题目要求输出罪犯编号或Cannot Determine或Impossible。 【程序清单】 下面是我校学生肖天同学写的程序。 {NOIP 2003 Problem 2 by Xiao Tian} {Test 0..9 : All right} {Algorithm : Enumerate the guilty and what day it is today. then we can determine whether each sentence is true or not.} Program logic; Const InName='logic.in'; OutName='logic.out'; MM=20; DayState:Array[1..7] Of String =('is Monday. #' ,'is Tuesday. #' ,'is Wednesday. #' ,'is Thursday. #' ,'is Friday. #' ,'is Saturday. #' ,'is Sunday. #'); cImpossible=-1; cCannotDetermine=-2; Var InFile,OutFile:Text; M,N,P:Integer; Name:Array[1..MM] Of String; Guilty:Array[1..MM,1..MM] Of Integer;{-1..1} WhatDay:Array[1..MM,1..7] Of Boolean; i,j:Integer; IsGuilty:Array[1..MM] Of Boolean; Ans,AnsC:Integer; Procedure Print(ID:Integer); Begin Assign(OutFile,OutName); Rewrite(OutFile); If ID=cImpossible Then WriteLn(OutFile,'Impossible') Else If ID=cCannotDetermine Then WriteLn(OutFile,'Cannot Determine') Else WriteLn(OutFile,Name[ID]); Close(OutFile); Halt; End; Procedure SetGuilty(CurID,NextID,NewValue:Integer); Begin If Guilty[CurID,NextID]=0 Then Guilty[CurID,NextID]:=NewValue Else If Guilty[CurID,NextID]<>NewValue Then Print(cImpossible); End; Procedure ReadStatement; Var i,j,CurID,NextID:Integer; CurStr,CurName:String; Begin For i:=1 To P Do Begin ReadLn(InFile,CurStr); CurStr:=CurStr+' #'; CurName:=Copy(CurStr,1,Pos(':',CurStr)-1); CurStr:=Copy(CurStr,Pos(':',CurStr)+2,255); CurID:=0; For j:=1 To M Do If CurName=Name[j] Then Begin CurID:=j; Break; End; If CurID=0 Then Continue; If CurStr='I am guilty. #' Then SetGuilty(CurID,CurID,1) Else If CurStr='I am not guilty. #' Then SetGuilty(CurID,CurID,-1) Else Begin CurName:=Copy(CurStr,1,Pos(' ',CurStr)-1); CurStr:=Copy(CurStr,Pos(' ',CurStr)+1,255); If CurName='Today' Then Begin For j:=1 To 7 Do If CurStr=DayState[j] Then Begin WhatDay[CurID,j]:=True; Break; End; End Else Begin NextID:=0; For j:=1 To M Do If CurName=Name[j] Then Begin NextID:=j; Break; End; If NextID=0 Then Continue; If CurStr='is guilty. #' Then SetGuilty(CurID,NextID,1) Else If CurStr='is not guilty. #' Then SetGuilty(CurID,NextID,-1); End; End; End; End; Function Check(CurGuilty,CurDay:Integer):Boolean; Var SayT,SayF:Array[1..MM] Of Boolean; i,j,TCount,FCount:Integer; Begin FillChar(SayT,SizeOf(SayT),False); FillChar(SayF,SizeOf(SayF),False); For i:=1 To M Do Begin If Guilty[i,CurGuilty]=1 Then SayT[i]:=True Else If Guilty[i,CurGuilty]=-1 Then SayF[i]:=True; End; For i:=1 To M Do For j:=1 To M Do If j<>CurGuilty Then Begin If Guilty[i,j]=1 Then SayF[i]:=True Else If Guilty[i,j]=-1 Then SayT[i]:=True; End; For i:=1 To M Do If WhatDay[i,CurDay] Then SayT[i]:=True; For i:=1 To M Do For j:=1 To 7 Do If j<>CurDay Then If WhatDay[i,j] Then SayF[i]:=True; For i:=1 To M Do If SayT[i] And SayF[i] Then Begin Check:=False; Exit; End; TCount:=0; FCount:=0; For i:=1 To M Do Begin If SayT[i] Then Inc(TCount); If SayF[i] Then Inc(FCount); End; If (FCount>N) Or (M-TCount<N) Then Begin Check:=False; Exit; End; Check:=True; End; Begin Assign(InFile,InName); Reset(InFile); ReadLn(InFile,M,N,P); For i:=1 To M Do ReadLn(InFile,Name[i]); FillChar(Guilty,SizeOf(Guilty),0); FillChar(WhatDay,SizeOf(WhatDay),False); ReadStatement; Close(InFile); FillChar(IsGuilty,SizeOf(IsGuilty),False); For i:=1 To M Do For j:=1 To 7 Do IsGuilty[i]:=IsGuilty[i] Or Check(i,j); AnsC:=0; For i:=1 To M Do If IsGuilty[i] Then Begin Inc(AnsC); Ans:=i; End; If AnsC=0 Then Print(cImpossible) Else If AnsC>1 Then Print(cCannotDetermine) Else Print(Ans); End. 第三题:加分二叉树 【试题分析】 一、题意分析 1、任务描述:在中序遍历为(1,2,3,…,n)的各种二叉树中,选出加分最高的一棵二叉树,输出最高加分和对此二叉树的前序遍历。 加分规则:任意棵子树subtree(也包含tree本身)的加分计算方法如下: subtree的左子树的加分*subtree的右子树的加分+subtree的根的分数 若某个子树为空,规定其加分为1,叶子的加分就是叶节点本身的分数,不考虑它的空子树。 2、输入:节点数和每个节点的分值。(n<30、分值<100) 3、输出:最高加分,和此树的前序遍历。(结果不会超过4,000,000,000)保险可利用double类型。 二、问题分析 1、因为给出中序遍历(1,2,3,…,n),所以可以以每个节点作根。当根为i时,则将序列分为三部分1至(i-1)为左子树,i为根,(i+1)至n为右子树。根据上面分析可断定此题可利用动态规划解决。 2、根据加分规则:转移方程如下: f(i,j)表示编号为i到编号为j的中序遍历的二叉树的最高加分。 f(1,n)即为所求最高加分。记录最高加分结果的划分就是前序遍历的结果。 可以利用备忘录形式或递推形式完成。 【程序清单】 下面是我校学生薛原同学写的程序。 {NOIPG2003第三题(2003年11月29日) 加分树 } program tree; var a:array[1..30] of longint; max:array[1..30,1..30] of longint; way:array[1..30,1..30] of integer; n:integer; f:text; procedure init; var f:text; fn:string; i:integer; begin readln(fn); assign(f,fn);reset(f); readln(f,n); for i:=1 to n do read(f,a[i]); fillchar(max,sizeof(max),0); close(f); end; procedure try(x,y:integer); var i:integer; begin if x>y then begin max[x,y]:=1;exit;end; if x=y then begin max[x,y]:=a[x];exit;end; for i:=x to y do begin if max[x,i-1]=0 then try(x,i-1); if max[i+1,y]=0 then try(i+1,y); if max[x,y]<max[x,i-1]*max[i+1,y]+a[i] then begin max[x,y]:=max[x,i-1]*max[i+1,y]+a[i]; way[x,y]:=i; end; end; end; procedure output(x,y:integer); begin write(f,way[x,y],' '); if x=way[x,y]-1 then write(f,x,' ') else if x<way[x,y]-1 then output(x,way[x,y]-1); if way[x,y]+1=y then write(f,y,' ') else if way[x,y]+1<y then output(way[x,y]+1,y); end; begin init; try(1,n); assign(f,'tree.out');rewrite(f); writeln(f,max[1,n]); output(1,n); close(f); end. 第四题:传染病控制 【试题分析】 一、题意分析 1、任务描述:在一棵树中,每层任意砍掉一条边,使包含根结点的树的结点数最少。 2、输入: 输入的第一行是两个整数n(1≤n≤300)和p,接下来p行,每一行有两个整数i和j,表示节点i和j间有边相连(即,第i人和第j人之间有传播途径相连)。其中节点1是已经被感染的患者。 3、输出: 只有一行,输出总共被感染的人数(即包含1号结点的树的结点数)。 二、问题分析 1、首先利用宽度搜索求出树的深度和每层的宽度,并记录每个结点的根结点编号。 2、再利用搜索,每层去掉一条边,记录感染人数。比较求出最少感染人数。 3、优化: 1)在搜索过程中可以利用剪枝加快搜索效率。目前我没有找到有效的剪枝方法,如游客一发信给我,谢谢。 2)杨博海同学利用随机方法求解,效果不错。即每层随机去掉一条边统计结点数,随即10000次输出最少感染人数。 【程序清单】 下面是我校学生杨博海同学写的程序。 Program Epidemic; Var root,list,short,s:array[1..300] of LongInt; e:array[1..600,0..1] of LongInt; a,b,c,n,m,t,best:LongInt; Procedure bfs; Var a,k:LongInt; Begin For a:=1 to n Do short[a]:=n; short[1]:=0; k:=1; list[1]:=1; s[1]:=1; For a:=1 to n Do Begin For b:=1 to m*2 Do If (short[e[b][0]]>short[e[b][1]]+1) and (short[e[b][1]]=a-1) Then Begin root[e[b][0]]:=e[b][1]; short[e[b][0]]:=short[e[b][1]]+1; Inc(k); list[k]:=e[b][0]; End; If s[a]=k Then Break; s[a+1]:=k; End; t:=a; End; Procedure calc; Var a,b,k,num:LongInt; flag:array[1..300] of Boolean; step:array[1..300] of LongInt; Begin flag[1]:=true; num:=1; For a:=2 to t Do Begin k:=0; For b:=s[a-1]+1 to s[a] Do Begin flag[list[b]]:=flag[root[list[b]]]; If flag[list[b]] Then Begin Inc(num); Inc(k); step[k]:=list[b]; End; End; If k>0 Then Begin flag[step[random(k)+1]]:=false; Dec(num); End; End; If best>num Then best:=num; End; Begin Randomize; Assign(input,'epidemic.in0'); Reset(input); ReadLn(n,m); For a:=1 to m Do Begin Read(e[a*2-1][0],e[a*2-1][1]); e[a*2][0]:=e[a*2-1][1]; e[a*2][1]:=e[a*2-1][0]; End; Close(input); bfs; best:=n; For a:=1 to 100000 Do calc; WriteLn(best); End.
展开阅读全文

开通  VIP会员、SVIP会员  优惠大
下载10份以上建议开通VIP会员
下载20份以上建议开通SVIP会员


开通VIP      成为共赢上传

当前位置:首页 > 百科休闲 > 其他

移动网页_全站_页脚广告1

关于我们      便捷服务       自信AI       AI导航        抽奖活动

©2010-2026 宁波自信网络信息技术有限公司  版权所有

客服电话:0574-28810668  投诉电话:18658249818

gongan.png浙公网安备33021202000488号   

icp.png浙ICP备2021020529号-1  |  浙B2-20240490  

关注我们 :微信公众号    抖音    微博    LOFTER 

客服