对于一个点 (x,y)(x,y)(x,y) ,先计算出在不碰到建筑物的限制下能取的最大半径,然后通过这个半径容易算出可以杀死多少个敌人。考虑对这个 (x,y)(x,y)(x,y) 退火。
然后如果直接把这个杀死敌人个数当作参考的话,这是个整值,导致整个二维函数很不平滑,模拟退火的效果会非常不好(形象理解一下,地图上大量充斥着 000 ,很可能走不出去)。
我们考虑设一个返回值为实数的平滑的函数,能对 「即使当前点杀死敌人数量是 000 ,那么它离 111 有多近」有良好的参考。「当前点对应的最大半径还需再增加多少能碰到第一个敌人」是一个好的选择,设它为 r0r0r0
,考虑将它纳入函数的组成部分。同时杀死敌人数量又不能不考虑,于是设这样一个估价函数 f(x,y)=−cr0 +cntf(x,y)=−cr 0 +cntf(x,y)=−cr0 +cnt ,其中 ccc 是待定系数,cntcntcnt 是杀死敌人数量,用该函数来退火。
那么显然要么 r0 =0r 0 =0r0 =0 要么 cnt=0cnt=0cnt=0。当 cnt=0cnt=0cnt=0 的时候,甚至可以证明 fff 在这些区域上是连续的(
另外看题解学到了新招:在退火的过程中可以将每次的答案都记录下来取 maxmaxmax,insteadofinstead ofinsteadof 只取最后一次。这样是严格优于只取最后一次的,因为这样甚至不带来任何时间复杂度上的增加。
经过辛苦的尝试,取 r0 =14.14,T0 =1e12,Te =10−8,ΔT=0.9996r 0 =14.14,T 0 =1e12,T e =10 −8 ,ΔT=0.9996r0 =14.14,T0 =1e12,Te =10−8,ΔT=0.9996 效果较好。考虑到函数有部分离散的存在,可以在卡时和只跑一次之间取个平均:跑个五次以内。而上述参数只允许跑 333 次。接下来就是随机种子的事情了。取 200606172006061720060617 跑两遍只有 #2 #3 没 ACACAC ,取 121212 跑一遍这两个点都 ACACAC 了,但是又有其他点没
ACACAC。那合起来跑三遍即可。
代码: