试题、程序及解题报告noip2004题目解析_第1页
试题、程序及解题报告noip2004题目解析_第2页
试题、程序及解题报告noip2004题目解析_第3页
试题、程序及解题报告noip2004题目解析_第4页
试题、程序及解题报告noip2004题目解析_第5页
已阅读5页,还剩79页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、NOIP2004题目解析河南省实验中学 罗宇龙津津的储蓄计划 津津的零花钱一直都是自己管理。每个月的月初妈妈给津津300元钱,津津会预算这个月的花销,并且总能做到实际花销和预算的相同。 为了让津津学习如何储蓄,妈妈提出,津津可以随时把整百的钱存在她那里,到了年末她会加上20还给津津。因此津津制定了一个储蓄计划:每个月的月初,在得到妈妈给的零花钱后,如果她预计到这个月的月末手中还会有多于100元或恰好100元,她就会把整百的钱存在妈妈那里,剩余的钱留在自己手中。 津津的储蓄计划 例如11月初津津手中还有83元,妈妈给了津津300元。津津预计11月的花销是180元,那么她就会在妈妈那里存200元,

2、自己留下183元。到了11月月末,津津手中会剩下3元钱。 津津发现这个储蓄计划的主要风险是,存在妈妈那里的钱在年末之前不能取出。有可能在某个月的月初,津津手中的钱加上这个月妈妈给的钱,不够这个月的原定预算。如果出现这种情况,津津将不得不在这个月省吃俭用,压缩预算。 现在请你根据2004年1月到12月每个月津津的预算,判断会不会出现这种情况。如果不会,计算到2004年年末,妈妈将津津平常存的钱加上20还给津津之后,津津手中会有多少钱。 样例一输入: save.in29023028020030017034050 90 80 20060 输出:save.out-7 样例二输入: save.in290

3、 230 280 200 300 170 330 50 90 80 200 60 输出:save.out1580 算法分析本题是NOIP2004中最简单的一道题目。只需要认真读题,理解题意。单纯的按照题目要求进行模拟即可。分析样例一129010002230800032800100100420001002005300002006170301003007340-108509901080112001260输出:-7月份 预算 现有 上缴 存储分析样例二129010002230800032800100100420001002005300002006170301003007330003008505020

4、050099060200700108080200900112008010010001260203001300输出:13001.2+20=1580月份 预算 现有 上缴 存储算法实现数据定义costi表示津津第i个月的花费sum表示津津在妈妈那里存的钱now表示津津当前手头的钱ans表示输出的答案算法实现程序框架读入数据主过程输出结果算法实现主过程(pascal版)Begin sum:=0; now:=0; for i:=1 to maxn do begin if now+300costi then begin ans:=-i; exit; end; now:=now+300-costi; te

5、mp:=now div 100; inc(sum,100*temp); dec(now,100*temp); end; ans:=now+trunc(sum*1.2);End;合并果子 在一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆。多多决定把所有的果子合成一堆。 每一次合并,多多可以把两堆果子合并到一起,消耗的体力等于两堆果子的重量之和。可以看出,所有的果子经过n-1次合并之后,就只剩下一堆了。多多在合并果子时总共消耗的体力等于每次合并所耗体力之和。 因为还要花大力气把这些果子搬回家,所以多多在合并果子时要尽可能地节省体力。假定每个果子重量都为1,并且已知果子

6、的种类数和每种果子的数目,你的任务是设计出合并的次序方案,使多多耗费的体力最少,并输出这个最小的体力耗费值。 输入输出样例输入: fruit.in3 1 2 9 输出:fruit.out15 数据规模 对于30的数据,保证有n=1000: 对于50的数据,保证有n=5000; 对于全部的数据,保证有n=10000。 算法分析看到这个题目我们都会很自然的想:如果每次都取当前数量最小的两堆果子进行合并,那么结果会不会就是最优的呢?对,答案是肯定的!并且我们可以运用数学归纳法来证明这种策略得到的结果就是我们所求的最优解。证明略。以上的思考过程就是一个贪心策略的过程。进一步的思考,实际上我们的方法与求

7、哈夫曼树的方法本质上是相同的!算法框架读入数据主过程 1.每次从当前果子堆中选择数量最少的两堆 2.累加体力消耗总和 3.将这两堆进行合并后再放回果子堆中。输出数据算法实现分析建立一个数学模型,我们所需要的操作就是从一个数列中取最小值,并且在进行操作后插入一个新的值。由于本题的数据规模达到了n=10000,所以怎样高效的实现取最小值和插入值成了本题的关键!算法实现一用数组和二重循环模拟实现。每次遍历大小为n的数组,找到最小的值。当找够两个最小值后求和,并将放入数组中的任意空位。算法一的代码实现(pascal版)Begin ans:=0; for i:=1 to n-1 do begin tem

8、p:=0; for j:=1 to 2 do begin min:=maxint; for k:=1 to n do if datakmin then begin min:=datak; minnumber:=k; end; inc(temp,min); dataminnumber:=maxint; end; inc(ans,temp); dataminnumber:=temp; end;End;算法一的效率分析时间复杂度 o(n2)空间复杂度 o(n)编程复杂度:低结论:由于最大数据为n=10000,所以这样的方法太慢了!那么有没有更好的算法呢算法二分析答案是肯定的!由于本题牵涉到了取最小值

9、的操作,我们自然想到了一种数据结构堆。那么什么是堆?它支持的操作有哪些?时间复杂度是多少?又应该怎么样实现呢?堆的定义堆是一棵近似满二叉树,它可分为大根堆和小根堆。堆的定义是递归的。堆的定义: 1.二叉树的每一个节点存储一个元素 2.二叉树的每一个非叶子所存储元素的优先级高于其儿子存储的元素的优先级。 堆的样例16478953以上就是一个小根堆的样例堆支持的三种基本操作插入值 Insert 取最小值 Getmin删除最小值 Deletemin 堆的插入16478953例如:插入一个值为2。插入的过程中始终维护堆性质2堆的插入16478953例如:插入一个值为2。插入的过程中始终维护堆性质2堆的

10、插入16478953例如:插入一个值为2。插入的过程中始终维护堆性质2堆的删除16478953例如:删除堆顶元素,并且始终维护堆性质。2堆的删除6478953例如:删除堆顶元素,并且始终维护堆性质。2堆的删除6478953例如:删除堆顶元素,并且始终维护堆性质。2堆的删除6478953例如:删除堆顶元素,并且始终维护堆性质。2堆的删除6478953例如:删除堆顶元素,并且始终维护堆性质。2堆的数组实现由于堆有一些特殊的性质,所以我们可以用数组来实现堆。tot保存堆中元素的个数heapi (1=i1时,heapi的父亲存放在heapi div 2当中所以任何时刻当堆不为空的时候,堆的最小元素始终

11、存放在heap1的位置。 堆的插入操作代码实现(pascal版)Procedure Insert(x:longint);var s:integer;begin inc(tot); heaptot:=x; s:=tot; while (s1)and(heapsheaps div 2) do begin swap(heaps,heaps div 2); s:=s div 2; end;End;堆的删除操作代码实现(pascal版)Procedure Delete;var s:integer;begin heap1:=heaptot; dec(tot); s:=1; while (s*2=tot)

12、do begin if (s*2=tot) or (heaps*2heapj then begin swap(heaps,heapj); s:=j; end else exit; end;end;堆的各种操作的时间复杂度分析由于用数组实现的堆时刻是一棵近似满二叉树,因此插入和删除的操作时间复杂度仅跟这棵二叉树的高度有关系。每次操作的复杂度为o(logN)取最小值操作的时间复杂度为o(1).有了堆这个强大的武器做保证,我们就可以快速的实现本题所需要的各种操作了!方法二主过程代码实现(pascal版)Procedure Main;var temp: longint;begin ans:=0; fo

13、r i:=1 to n-1 do begin temp:=data1; delete; inc(temp,data1); delete; inc(ans,temp); Insert(temp); end;end;算法二的效率分析时间复杂度 o(nlogn)空间复杂度 o(n)编程复杂度:一般结论:由于最大数据为n=10000,所以这样的方法完全可以对付所有的数据!那么有没有更好的算法呢算法三分析答案是肯定的!注意到题目中的一个性质:每一次合并过的果子堆的重量是递增的。题目中给出果子范围=20000.如果能够从分的利用好了这两个条件,我们将得到一种更优的算法!算法三分析这种算法的基本思想是:利用

14、有序队列!首先,将所有果子数读入。由于果子数范围小,我们可以利用桶排序在o(n)的时间内构造一个有序队列count同时我们把每次新产生的堆的数目加到另外一个队列seq中,使seq也保持递增的性质。这样每次的最小值只有可能在seq或者count的队首取得。每次取最小值和删除最小值的时间复杂度为o(1)当新产生一对果子后,利用新产生果子递增的性质,我们可以直接把它加在队列seq的队尾。时间复杂度为o(1).方法三演示例如当n=7,果子堆数分别为7 15 2 4 6 10 5 时,我们先进行第一步工作,用桶排序构造一个初始有序序列count. 456710152方法三演示初始数据队列count456

15、710152新增数据队列seqAns=0方法三演示初始数据队列count456710152新增数据队列seqAns=0合并6方法三演示初始数据队列count5671015新增数据队列seqAns=66方法三演示初始数据队列count5671015新增数据队列seqAns=66合并11方法三演示初始数据队列count671015新增数据队列seqAns=1711合并13方法三演示初始数据队列count1015新增数据队列seqAns=301113方法三演示初始数据队列count1015新增数据队列seqAns=301113合并21方法三演示初始数据队列count1615新增数据队列seqAns=

16、511321方法三演示初始数据队列count1615新增数据队列seqAns=5113合并2821方法三演示初始数据队列count1628新增数据队列seqAns=7921方法三演示初始数据队列count212849新增数据队列seqAns=79合并方法三演示初始数据队列count49新增数据队列seqAns=128算法三代码实现桶排序(pascal版)Procedure Init;Begin Fillchar(count,sizeof(count),0); readln(n); for i:=1 to n do begin read(j); inc(countj); end; point:=

17、1;end;counti表示大小为i的数据的个数。point是count队列的指针。算法三主过程代码实现(pascal版)Procedure Main;var a,b,temp :longint;begin ans:=0; for i:=1 to n-1 do begin temp:=0; for j:=1 to 2 do begin a:=Get_count; b:=Get_seq; if ab then begin inc(temp,a); delete_count; end else begin inc(temp,b); delete_seq; end; end; inc(ans,tem

18、p); Insert_seq(temp); end;end;算法三队列操作代码实现(pascal版)Function Get_count:longint;begin while (countpoint=0) and (point20001) do inc(point); if point20001 then Get_count:=point else Get_count:=maxlongint;end;Procedure Delete_count;begin if point20001 then dec(countpoint);end;Procedure Insert_seq(x:longin

19、t);begin inc(r); seqr:=x;end;Function Get_seq:longint;begin if l=r then Get_seq:=seql else Get_seq:=maxlongint;end;Procedure Delete_seq;begin if l=r then inc(l);end;算法三的效率分析时间复杂度 o(n)空间复杂度 o(n)编程复杂度:一般!结论:这算法在各个方面都非常的优秀那么有没有更好的算法呢算法三小结算法三在的时间复杂度在理论上已经是下界了,但是希望大家再思考,创造出同样优秀的算法。_另外,这道题目虽然简单,但是在当时却害了不少

20、人。有很多同学没有看清楚题目要求,把这个题目做成了石子合并,败的很惨-_-b包括本人。希望大家吸取教训。合唱队形 N位同学站成一排,音乐老师要请其中的(N-K)位同学出列,使得剩下的K位同学排成合唱队形。 合唱队形是指这样的一种队形:设K位同学从左到右依次编号为1,2,K,他们的身高分别为T1,T2,TK,则他们的身高满足T1.Ti+1TK(1=i=K)。 你的任务是,已知所有N位同学的身高,计算最少需要几位同学出列,可以使得剩下的同学排成合唱队形。输入输出样例输入: chorus.in8186 186 150 200 160 130 197 220输出:chorus.out4样例分析186

21、186 150 200 160 130 197 220 ans=4186 186 150 200 160 130 197 220 ans=4186 186 150 200 160 130 197 220 ans=4算法分析题目要求出列的人数最少,也就是等价于使队列中的人数尽可能的多。考虑站在“中心位置”的同学,如果他确定了,怎样使得队列中的人数尽可能的多呢?显然,必需使得他左边的那部分队列尽量长,同时他右边的那部分队列尽量长。那么怎样使得他左边的队列尽量长呢?算法框架最长不下降序列!利用这个思想我们得到了算法的主框架如下:1.用动态规划求出以每个人结尾的左边和右边的最大队列长度。2.枚举每个人

22、为“中心位置”,计算出满足题目要求的队列长度,记录最大值。算法分析我们用lefti表示从左边起到第i个人结束的最长上升队列的人数,那么得到状态转移方程:lefti=maxmax(leftk)+1,1 1=k=i-1 heighkheighi同样的,用righti表示从右边起到第i个人结束的最大上升队列的人数,得到:righti=maxmax(rightk)+1,1 i+1=k=n heighkheighi算法分析对于样例,得到的left,right值如下表:数据186186150200160130197220left11122134right33232111算法实现(pascal版)Proce

23、dure Main;begin for i:=1 to n do begin lefti:=1; for j:=1 to i-1 do if (datajlefti) then lefti:=leftj+1; end; for i:=n downto 1 do begin righti:=1; for j:=n downto i+1 do if (datajrighti) then righti:=rightj+1; end; ans:=0; for i:=1 to n do if (lefti+righti-1)ans then ans:=lefti+righti-1;end;算法效率分析时

24、间复杂度 o(n2)空间复杂度 o(n)编程复杂度:简单结论:这种算法足以应对所有的测试数据那么有没有更好的算法呢算法小结答案是肯定的!如果换一种状态表示,我们可以在二分查找的帮助下以nlogn的时间复杂度解决本题。请有兴趣的同学自己进行研究学习。由于本题数据规模比较小,所以算法多种多样。在比赛的时候有人用枚举过了80%的数据,也是很好的。这就提醒我们要放开思路,对于每一题都要尝试着用不同的算法去解决,分析。虫食算所谓虫食算,就是原先的算式中有一部分被虫子啃掉了,需要我们根据剩下的数字来判定被啃掉的字母。来看一个简单的例子: 43#9865#045+ 8468#6633 4444 5 506

25、978其中#号代表被虫子啃掉的数字。根据算式,我们很容易判断:第一行的两个数字分别是5和3,第二行的数字是5。 虫食算 现在,我们对问题做两个限制: 首先,我们只考虑加法的虫食算。这里的加法是N进制加法,算式中三个数都有N位,允许有前导的0。 其次,虫子把所有的数都啃光了,我们只知道哪些数字是相同的,我们将相同的数字用相同的字母表示,不同的数字用不同的字母表示。如果这个算式是N进制的,我们就取英文字母表的前N个大写字母来表示这个算式中的0到N-1这N个不同的数字(但是这N个字母并不一定顺序地代表0到N-1)。输入数据保证N个字母分别至少出现一次。 虫食算 BADC+ CRDA DCCC上面的算

26、式是一个4进制的算式。很显然,我们只要让ABCD分别代表0123,便可以让这个式子成立了。你的任务是,对于给定的N进制加法算式,求出N个不同的字母分别代表的数字,使得该加法算式成立。输入数据保证有且仅有一组解,输入输出样例输入: alpha.in5ABCEDBDACEEBBAA输出:alpha.out1 0 3 4 2算法分析看到题目我们的第一想法是:穷举每一个字母所有可能的取值,然后判断等式是否成立。然而这样的时间复杂度是o(n!),当n=26时显然会超时。所以我们要改进方法。那么怎样的方法才好呢?DFS+强剪枝!算法分析我们首先要确定的是搜索的顺序。那么是按照A,B,这样的顺序搜索,还是按

27、照字母在算式中出现的顺序计算呢?显然后者比较好,因为它可以帮助我们在搜索的过程中进行剪枝。因此我们确定了程序的第一步,构造出一个搜索顺序。按照从右到左字母在算式中的出现次序构造seq。算法实现构造搜索序列seqProcedure Creat_seq;begin seq:=; Fillchar(fz,sizeof(fz),true); for i:=n downto 1 do begin if fzs1i then begin fzs1i:=false; seq:=x+seq;end; if fzs2i then begin fzs2i:=false; seq:=x+seq;end; if fz

28、s3i then begin fzs3i:=false; seq:=x+seq;end; end;end;算法实现数据定义ans A.Z 表示最终答案s1,s2,s3 string 表示输入的三个字符串x1,x2,x3 1.26 表示对应的算式number 1.26 of boolean 表示对应的数字是否出现finish 表示搜索是否结束算法实现程序框架这样我们就得到了我们的主程序框架.Procedure search(dep:integer);var j:integer;begin if check then exit; /判断当前的算式是否出现矛盾 if finish then exit

29、; if dep=0 then begin /输出答案 finish:=true; Print; exit; end else begin for j:=n-1 downto 0 do if numberj then begin numberj:=false; ansseqdep:=j; paint(seqdep,j); search(dep-1); numberj:=true; ansseqdep:=0; paint(seqdep,-1); end; end;end;算法实现剪枝那么怎样利用判断算式是否合法来剪枝呢?我们看下面一个算式,其中?表示未知的字母. 例如 n=8,当前的算式为 4?3?7?1 + 5?3?5 ?5?5?7 1.末尾数15显然不等于7,程序便可以剪枝 2.3+?=4 ?只可能是1或者2,但是它们都已经出现过了,所以可以剪枝。 3.首位4+5=n,可以剪枝。算法实现剪枝(1)Function check:boolean;var p1,p2 : integer;begin check:=false; for i:=n downto 1 do begin /case one a + b = c if (x1i-1) and (x2i-1) and (x3i-1) then begin

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论