hello大家好呀,我是小楼。
上周参加了一个区的程序员技能比赛的初赛,其实就是算法比赛,虽然最后结果是过了初赛,但过程真是一言难尽啊。
这次的算法比赛和ACM非常类似,虽然我大学是数学专业,虽然大学也修过ACM这门课,但是我的算法是真的不行,很菜很菜的那种。
好在这次比赛是组(抱大腿)队模式,3人一组,3个小时时间,一共7道算法题,1入门,2简单,2中等,2困难。
10分钟写出入门题,但…
由于我知道我比较菜,所以比赛一开始,我就挑了一个看起来最简单的题目做,难题交给队友。
结果是3个小时过去,这个看起来最简单的题目,愣是没有做出来,下面就结合这道题讲讲我的心路历程。
这道题的描述是这样的:
看起来文字很多,其实要表达的很简单,就是输入一些成绩,每个成绩输进去时,如果超过全班最好成绩则输出prefect,如果超过自己的最好成绩则输出great,如果没超过自己最好成绩则输出bad。
是不是很简单?用一个max变量保存全班最好成绩,用一个map保存每个人的最好成绩,不就解决了吗?
不过这是我第一次用这个oj系统,连用户都是刚注册的,所以我还特地看了一会输入输出的demo,这次比赛只能使用ACM的输入输出模式,例如如果用的是Go语言,输入输出应该是这样:
当一些key存入map时,会先对key计算hash值,在map中找到对应的hash槽,这个槽之后一般是个链表(有的语言也会做一些优化成树状,这里我们简化为链表),因为不同的key的hash值可能会重复(冲突),冲突了只能把key排成一个链表,每次查找时都要遍历链表。
所以有没有可能,设计者给出了一堆hash值重复的name,数量又多,导致每次插入、查找时都要遍历链表,性能下降,导致超时?
于是再仔细审题,我发现输入的姓名和成绩是有限制的:
name保证长度不超过6,仅由小写英文字母组成,每个名字代表唯一一个同学
x为1位小数,0≤x≤300
name最长为6,且为小写字母,这点给了我一点启发,能不能让查询map变成O(1)复杂度?
显然可以的,小写字母范围为a~z,如果看成数字就是1-26,也就是27进制,所以每个name可以表示为一个27进制的数,这样就可以把所有人的成绩放到一个大数组里去,按name的27进制进行O(1)的查找。
为什么是27进制而不是26,因为name没说是多少位,比如只有5位,那空出的一位怎么表示?只能用0表示了,a-z就是1-26,合起来是27进制。
参考10进制计算法则,27进制应该这样计算(以roshi为例):
结果竟然报错了,我当时不理解,事后理解了,我们暂且不说,后面会说到原因。
就是因为这个报错不明不白,明明能测试通过,到底哪里理解有偏差?亦或是内存超了?
优化内存占用
上面的代码用到了2个数组,一个存最大值,一个存值是否存在,一个数组是1513361KB,2个就是3026722KB,是最大内存限制的5.7倍
var scores [387420488]int32var exist [387420488]int32
exist数组可以用boolean类型,分数最大值0<=0<=300,int16足矣
大小 范围 int8 1字节 -128 ~ 127 int16 2字节 -32768 ~ 32767 int32 4字节 -2147483648 ~ 2147483647
如果是这个组合,将占用 1135020KB,是上限的2倍多,还是有点超,先试试:
还是一样,难道是我算法有问题?没道理啊。到这里我实在是没招了,3小时也耗尽了,比赛结束。
赛后思考
赛后,我拿着这道题去找了一位刚入职字节的朋友,想着刚去字节应该刷过不少题吧,果然大佬就是大佬,给出了一个有新意的思路,用前缀树做:
最后
过程虽然曲折,但最终还是解决了这个入门题,而且还尝试着用几种方法来解,虽然不尽如人意,但终究还是有点收获。
当然我们组的小伙伴也很给力,做出来3道题,我们最终的成绩是排名进了前10%,虽然我只贡献了一点点(没完全做出来也有得分,按通过的用例算,我这题大概拿到了90%的分),也算是可以了,而且还有一道题也可能是因为这个输入被卡了,所以如果这两道卡的题都做出来,估计排名能进前三。
初赛算是过了,接下来准备复赛,如果复赛还有好玩的事情,我再来写一篇文章,哈哈。
这一言难尽的比赛,大家给个赞鼓励下吧。
搜索关注”捉虫大师”,后端技术分享,架构设计、性能优化、源码阅读、问题排查、踩坑实践。
如发现本站有涉嫌抄袭侵权/违法违规等内容,请联系我们举报!一经查实,本站将立刻删除。