第一次参加Imagine Cup的算法比赛。做到最后有些力不从心,基本放弃。最终做到总分接近3.2k,位列87。
最初参赛的目的很简单,为自己看高大爷的书提供点动力。没想到的是Herbert真不是白给的,大把时间砸了上去还是没搞定(大爷的书一眼没看*_*)。Match 1 结束后,看了一下名列前茅的小N孩们的solutions。OMG,不由不赞,很有想法。
于是决定找时间参加Match3或Match4(先休息一下,看看大爷的书,这叫什么来自,训赛结合么)。目的又多了一点,活动活动思维有点僵化的脑子。BTW,这一次不再是有任何玩的心态,好好总结,好好思考,一定要晋级round 2的说。
总结一下第一轮的战况。最后有5道题没有slove。三道完全没思路,有两道不知道如何压缩。已经做出来的,和别人做到的最优solution相比还是相差甚远。相比而言,一方面是很多有效的实现技巧没想出来(脑袋僵化了。。。经验太少了。。。),有的是应该想到而没想到有的是完全超出我脑力思考范围(天才+电脑才能琢磨出的方式,我学习学习就OK。。。);另一方面,是有些规律看不出来(其实,这种时候很少),或者看的不够透彻(这种比较的多,还是脑子有点僵了。。。)。
于是,看了些别人的解法,总结了一下。当然,总结只是最初步的脑部运动,要想走的更远,做的更好,还要继续多思考多深化。MS这种能力,现在欠缺了一些,需要多练多想多学的说。不论如何,先走第一步先,做个简单的思考和总结。
基本实现技法
回忆一下最开始,做递增是老老实实的做f(N):sf(N-1)。后来,看了winshare同学的解法,才知道这是多么的SB。
一般满足关系式f(n) = f(n-1) + const的渐变式要这么做:
【F1】(F1表示公式1,下同)
a(A):Aa(A[cosnt action])
a()
相关题目很多,比如m1.3(m1表示match1,3表示第三题,下同)
这个渐变有个特点,就是后一个动作较前一个动作相比做了个常变化。如果是渐变的动作和当前迭代的次数相关(比如说,变差的序列,树型的递归),既满足f(n) = f(n-1) + g(n)这个关系式的,需要引入多一个的变量:
【F2】
a(A,B):Aa(AB,B[const action])
a(,)
比如m1.22。
上面两种渐增算法的特点就是奔着无穷去,一定只能在算法的最后使用(non-stop),而且无法在主序列上做减(F2其实相当于在此序列上做减,因为An包含了An-1和B的行为),在主序列做减,且满足f(n) = g(N-n) && g(n)递增 可以考虑以下公式:
【F3】
a(A,N):a([const action]A,N-1)A
a(,[some int])
比如m1.13.
这个算法相当于把一些复杂的动作提前做了,并且把做动作的函数(g(n))放到了计数的函数中,减少了函数定义的成本(至少有3个字节的节约)。
在F3中,用到了一种常用的手段:
【T1】(T1表示技巧1,下同)
把主要用于为某个函数提供基础行为的函数,合并作为参数来提供。
比如m1.7 by chokudai(by someone 表示这种解法不是很共性,主要是someone的解决方案中用到,下同)
合并过来的函数的特点就是基本不出现在主体中,而是在参数中,原上层函数的主体基本移入参数中,新函数主体变得简单。
同时,在一些函数合并时,由于有的函数的存在还顺便为其他函数提供了帮助,这就需要有另一种技巧:
【T2】
尽量使用已有的函数,用可能很复杂的方式实现一种原本简单的路线。
这种情况常用在机器人出发点不在循环的起点上,且循环是以形如a(A):AAAA这种固定次数循环模式出现的时候(不是死循环)。
有时候,这要求你需要看的很远,为了达到到达出发点的目的忽视所有的灰点:
【T3】
不要被灰点所迷惑,只要函数循环大于N次后能不再触碰灰点或达到稳定,并正确求解的话,忽视之前的灰点。
比如m1.1 by Jamal, m1.7 by chokudai, m1.12。都是这类用法的典范。这就像做归纳法一样,忽视初始值,从N开始正确就可以。一定要把眼光放远一点再远一点。。。
即使如此,有的时候你无法用原有的函数做到一些事情,并且你发现你的需求与所给函数只差一点点,那么考虑下面的一个技巧:
【T4】
当主函数不能完全满足需求时,添加一个参数,并在主要调用该函数的场景,将该参数放空。
比如m1.7 by Rac, m1.11 by Rac, m1.14 by chokudai。我们可以分析一下函数付出的代价,当一个函数由a(A):AAA变成a(A,B):AABA时,增加了[B出现次数]+1个字节的开销。在原有函数调用时(有a(A)变成了a(A,))不会增加任何开销。同时,这可能减少其他函数更多字节的开销,可视为经济之道。
还有些技巧属于启发式的。比如:
【T5】
在90度旋转对称的图形中,使用3,5等基数次作为a(A):AAA的重复次数。
比如m1.10, m1.11 by Rac。虽然有时候会有些冗余,但无所谓,他不会陷入偶数次那种无法遍历到的状态(或者需要中途停下转向)。
【T6】
形如a:ssb,t:at之类的取代型函数,要仔细核对其开销。
比如m1.19。
eg.
f(N):sf(N-1)
t:f(9)t
t
==>
f(N):sf(N-1)f(9)
f(9)
【T7】
注意转向动作出现在循环自动作的头和尾时的连续性,利用这种连续性可以做掉头的复杂动作。
比如:m1.12。
【T8】
当函数中出现多个动作参数时,仔细观察这些参数动作的连续性,尝试合并。
比如:m1.6 by Rac
图形观察与模式
很大一部分的问题,用上面的技巧可以搞定。但还有一些问题就不行了。而这类问题往往更难开窍。
【T9】
当已有规律无法满足需求的时候,在转弯、掉头等视角上进一步思考,提取更普遍的规律。
比如:m1.4
很多时候,能看到规律,但是看到的还不够。是因为,我们的视角太局限了。扩大范围,想转向和掉头扩展。说不定会有更好的发现。这需要耐心耐心再耐心,求变求变再求变。。。(*_^原来我喊口号如此有天分)
当然还有些图形,无论你怎么看都看不出规律,因为它们被称为模式。他们需要用一些超出人脑极限的复杂做法去碰,去尝试。
有一种模式被称为随机模式:
【P1】(P1指模式1,下同)
f(A):Af([a combination of x, l, r, A])
f(A)
eg.
f(A):Af(lArAs)
f()
比如:m1.1 m1.20
它应用的场景主要是大量灰点,大量墙壁的场合。不仅用于有些无法观察规律的图形,还可以投机取巧放在某些有规律图形中。它的特点是超出人脑想象,基本无法预测,而好处就是,一旦搞定,字节数暴降,分数大把大把来。
还有一种模式,清晰了许多,主要用于存在正方形的场合:
【P2】
f(N):{slsr,lsrs,srsl,rsls}f(N-1){ssl,ssr,lss,rss,srs,lsl}
c:f(N)f(M)f(K)c
c
比如:m1.18。
对于这个模式,我无话可说,其中对T3、T5和对中嵌套递归的应用都堪称典范。对创建该模式的人Orz。。。
还有一种是形如模式:
【P3】
比如:m1.21 by Rac
f(X):XXf(Xllsrsr)
f(l)
比如:m1.1 by Jamal
f(A):AAf(Aslsr)
f(r)
路线很PP,而且我完全看不出是如何想出来的。。。
对于模式,最好的使用simu。simu vs man 估计是Imagine Cup中一个永恒的话题。有时间,我也去搞定一个simu。。。嘎嘎。。。
试题回顾
这一部分对我对每一题的解法做一个整体的评定,以增强印象,方便日后回忆。
L1. 无论是哪种NB解法都有模式之嫌,死得其所。
L2. 。。。
L3. [F1] Good job。
L4. Unslove one。没有把规律提升[T9],BS自己。。。
L5. 规律看的比较清楚,实现上的问题应该问题不大。还不错。
L6. 参数合并的问题[T8],滔滔江水一把。
L7. 寻找起点的问题[T3]。
L8. 很有意思的题目,做的还算不错。
L9. 实现细节上的问题,规律把握的不错,赞一个。
L10. 还算做的OK。
L11. 寻找起点[T3]和看规律的问题T[9]。需要总结。
L12. 需要渐变达到稳定后进行处理[T3],非常有学习价值。
L13. 递减[F3]公示的应用,要学会。没有winshare GG,就还有看规律的问题了[T9]。。。
L14. 起点问题[T3],其他还都OK。
L15. 。。。
L16. 。。。
L17. Unslove one && unslove one for ever。。。
L18. Unslove one && [P2]。。。
L19. 还是被灰点迷惑了。。。[T3]
L20. Unslove one && [P1]。。。
L21. 最好的解法是[P3]。其他没得说了。。。
L22. Unslove one && [F3]。。。
L23. Good job。
L24. 实现上的技巧了。。。
L25. 也算做的不错了。。。