欢迎光临散文网 会员登陆 & 注册

【TIS-100 攻略】TIS-NET 第 16 关:反向索引查询器

2022-11-09 13:40 作者:ココアお姉ちゃん  | 我要投稿

本文首发于 B 站《TIS-100》文集(https://www.bilibili.com/read/readlist/rl626023)。原创不易,转载请注明出处。

TIS-NET 第 16 关《反向索引查询器》(Back Reference Reifier)关卡展示

本关的 IN.A 会不断给你提供一些数字,然后:

  • 当你从 IN.R 里收到 0 时,你需要向 OUT 输出 IN.A 中的当前数字;

  • 当你从 IN.R 里收到 -1 时,你需要向 OUT 输出 IN.A 中的前 1 个数字;

  • 当你从 IN.R 里收到 -2 时,你需要向 OUT 输出 IN.A 中的前 2 个数字;

  • 当你从 IN.R 里收到 -3 时,你需要向 OUT 输出 IN.A 中的前 3 个数字;

  • 当你从 IN.R 里收到 -4 时,你需要向 OUT 输出 IN.A 中的前 4 个数字。

  • IN.R 的最小值为 -4,也就是最多查询到前 4 个数字。

我们可以借用第一章第 18 关《信号窗口筛选器》里的【滑动窗口】算法思想,维护最近 5 个值,并选择请求的值输出即可(传送门:【TIS-100 攻略】第 18 关:信号窗口筛选器)。问题在于,本关的版面上没有提供任何栈,所以我们只能通过节点来存储这最近的 5 个值。设当前值为 a[0],前 4 个值分别为 a[-1] ~ a[-4],那么以下的示意图里提供了一种维护最近 5 个值的方法:

节点 B 存储 a[-4] 和 a[-3],节点 B 存储 a[-2] 和 a[-1],IN.A 存储 a[0]
第 1 步,节点 B 向节点 C 发送 a[-4]
第 2 步,节点 B 从节点 A 处接收 a[-2],a[-4] 被舍弃
第 3 步,节点 A 从 IN.A 处接收 a[0]
第 4 步,IN.A 变为下一个数字 a[1],节点 B 向节点 C 发送 a[-3]
第 5 步,节点 B 向节点 C 发送 a[-2]
第 6 步,节点 A 向节点 C 发送 a[-1]
第 7 步,节点 A 向节点 C 发送 a[0]
第 8 步,节点 A 和节点 B 都将自己的 acc 与 bak 互换

至此:

  • 当前数字变为了 a[1];

  • 节点 A 的 acc 为当前数字的前 2 个数字 a[-1],bak 为当前数字的前 1 个数字 a[0];

  • 节点 B 的 acc 为当前数字的前 4 个数字 a[-3],bak 为当前数字的前 3 个数字 a[-2]。将以上下标全部减去 1,我们就又回到了第一步时的样子。

节点 C 会依次收到 a[-4] ~ a[0],但我们只能选择其中 1 个量输出,其余 4 个量都要丢弃。那么我们在输出之前,要丢弃掉多少个量呢?

  • 当 R 为 0 时,我们要输出 a[0],要丢弃前方的 a[-4] ~ a[-1] 共 4 个量;

  • 当 R 为 -1 时,我们要输出 a[-1],要丢弃前方的 a[-4] ~ a[-2] 共 3 个量;

  • 当 R 为 -2 时,我们要输出 a[-2],要丢弃前方的 a[-4] ~ a[-3] 共 2 个量;

  • 当 R 为 -3 时,我们要输出 a[-3],要丢弃前方的 a[-4] 共 1 个量;

  • 当 R 为 -4 时,我们要输出 a[-4],没有量可以丢弃。

因此,输出前要丢弃的量 n 和 R 的关系为:

如果输出的不是 a[0],那么输出完毕后,剩余的量也要丢弃。不过这部分量我们可以用【监测哨兵】的方法来丢弃:上方的节点将 a[-4]~a[0] 全部发送完毕后,最终发送一个哨兵 0。当节点 C 接收到最终的哨兵 0 时,停止丢弃。本关的思路就是这样,代码如下:

IN.R 下方的节点将收到的 R 值传给右边的 A 节点(mov up right)。

A 节点和 B 节点首先接力将本次的 R 传给 C 节点(mov left down, mov up down)。接下来 A 节点和 B 节点按照上方示意图的指示,向 C 节点发送 a[-4] ~ a[0] 的值。

首先是 B 节点,先传 acc 里的 a[-4](mov acc down),并接收 A 节点传来的 a[-2],覆盖掉原先的 a[-4](mov up acc)。然后传 bak 里的 a[-3](swp, mov acc down, swp)和 acc 里的 a[-2](mov acc down)。接着,A 节点会将 a[-1] 和 a[0] 发过来,我们原封不动地将这两个数传下去(mov up down, mov up down)。最后,发送哨兵 0(mov 0 down),并将 acc 和 bak 交换(swp)。

A 节点简单一些,先传 acc 里的 a[-2](mov acc down),然后从 IN.A 接收 a[0],覆盖掉原先的 a[-2](mov up acc),接着传 bak 里的 a[-1](swp, mov acc down, swp)和 acc 里的 a[0](mov acc down),最后将 acc 和 bak 交换(swp)。

C 节点按照计划,首先丢弃前 4+R 个量:

  1. 将 acc 设为 4+R(mov 4 acc)

  2. (add up)

  3. 若 4+R 为 0,则不丢弃任何量,直接跳到第 7 行(jez 7),

  4. 否则每丢弃一个量(mov up nil),

  5. 就令 acc 减去 1(sub 1),

  6. 一直如此做直到 acc 减到 0 为止(jnz 4)。

  7. 将该丢弃的量悉数丢弃后,将下一个量送到输出口(mov up down),

  8. 然后继续丢弃剩下的量(mov up acc),

  9. 直到遇到哨兵 0 为止(jnz 8)。

点击左下角的【RUN】,稍等片刻,便会弹出结算界面:

也许我以上说的前 4 个数的维护方案不是最优的,我的运行时长离直方图最左端还有不少距离。如果读者有更好的维护方案,请留言告诉我。

【TIS-100 攻略】TIS-NET 第 16 关:反向索引查询器的评论 (共 条)

分享到微博请遵守国家法律