NOIP2004 解题报告

第一题:
津津的零花钱一直都是自己管理。每个月的月初妈妈给津津300元钱,津津会预算这个月的花销,并且总能做到实际花销和预算的相同。

    为了让津津学习如何储蓄,妈妈提出,津津可以随时把整百的钱存在她那里,到了年末她会加上20%还给津津。因此津津制定了一个储蓄计划:每个月的月初,在得到妈妈给的零花钱后,如果她预计到这个月的月末手中还会有多于100元或恰好100元,她就会把整百的钱存在妈妈那里,剩余的钱留在自己手中。

    例如11月初津津手中还有83元,妈妈给了津津300元。津津预计11月的花销是180元,那么她就会在妈妈那里存200元,自己留下183元。到了11月月末,津津手中会剩下3元钱。

    津津发现这个储蓄计划的主要风险是,存在妈妈那里的钱在年末之前不能取出。有可能在某个月的月初,津津手中的钱加上这个月妈妈给的钱,不够这个月的原定预算。如果出现这种情况,津津将不得不在这个月省吃俭用,压缩预算。

    现在请你根据2004年1月到12月每个月津津的预算,判断会不会出现这种情况。如果不会,计算到2004年年末,妈妈将津津平常存的钱加上20%还给津津之后,津津手中会有多少钱。  

解题过程:
送分题,模拟即可,但是要注意最后结算的时候除了存的钱,别忘记加上12个月累积下来没有花掉的钱;
15分钟左右一次AC;
 

第二题:
题目大意:给出n堆果子和各自的重量,要两两合并成一堆,合并的代价是两堆的重量和,求最小的总合并代价。

解题过程:
老题目,每次选取最小的两堆合并,直接循环找O(n^2)必定是超时的,因此可以用小根堆优化,但是手写堆有些麻烦。在建兰培训时提到过一种 更好的方法:
开两个队列,初始队列1为所有堆的重量,合并后的重量放到队列2里,每次取出两个队列队首小的那个的,重复一次,将两个值合并放到队列2里。

 

第三题:
题目大意:
让一个队伍尽可能少的人出队,使队伍排成以某个同学为最高,向左右递减。

解题过程:
直接分别从左到右,从右到左求以第i个人为结尾的最长上升子序列,在枚举i取最大值即可。时间复杂度O(n^2+n),或者用另外一种方法二分优化到(Onlogn+n);

第四题:(较难)
题目大意:给出N位的竖式加法,每一位都用字母表示,求出字母代表的数字;(输入数据保证唯一解)

解题过程:
一看便是搜索题,但n最大有26,直接枚举字母代表的数字要O(n!)的时间,要加适当的剪枝。
基本算法是回溯,记录字母代表的数字,和每个数字是否用过,从右往左处理每一列,如果碰到字母未确定,就枚举+dfs,并记录进位的情况,如果同一列有2个字母已经确定,那么可以直接计算另外一个,有冲突的话就回溯;
不加任何剪枝能过9个点,50分钟左右写完。
剪枝:由于题目数据保证只有唯一解,用一个flag记录是否已经找到答案,flag=1的时候就直接return;能过10个点。
优化1:枚举不确定的字母的值得时候,改为从n-1到0(原来是从0到n-1),这样效率要快得多,能过11个点,第12个点1100ms左右,就差一些。
然后就卡了N久,百度了半天感觉所有剪枝都用过了,结果卡在了1029ms,就是过不去。。。
最后抱着侥幸心理把string 改成了char数组,一提交居然600ms+AC了。难道取出char数组中的一个元素要比取出string中的一个元素快得多?百度了一下,可惜都没有说这个。八成是这样了。
被最后一个点恶心了2个小时。正道题估计3个小时才AC。
 
收获:
训练了耐心。。第一次写出5000B+的代码。。
DFS时循环变量从小到大改为从大到小可能会大大加速程序运行时间。

posted @ 2014-08-15 07:26  lzw4896s  阅读(383)  评论(0编辑  收藏  举报