uoj#P163. 【清华集训2015】新式计算机
【清华集训2015】新式计算机
这是一道提交答案题。
伟大的人类智慧之神 clevertick 奋战三星期,造出了一台新式计算机(其实他造计算机只用了三天,剩余的时间都在编译、调试……),起名 CTOX。现在,他可以抛弃自己旧的机器,来用 CTOX 完成自己的工作了……
CTOX 拥有足够大(可以认为是无穷大)的内存,并且它的内存地址是二维的,也就是说,一个内存地址可以使用一对整数 $(x, y)$ 来表示,和这个地址相邻的4个地址分别是 $(x+1, y)$(右)、$(x, y+1)$(上)、$(x-1, y)$(左)、$(x, y-1)$(下),每个内存地址都可以存储一个 $0 \sim 65535$ 范围内的整数值。然而,CTOX 没有寄存器,只有一个空闲的开关可以用来存储临时数据(既然是开关,那就只有开与关两种状态了)。CTOX 每次只能读写一个内存地址,它用一个指针来指向目前正在读写的内存地址,读写指针每次从一个地址移动到与它相邻的某个地址称为移动一单位。CTOX 每次启动时,都会初始化——清空所有内存(即将所有内存中存储的值全部清零)、关上这个空闲开关、读写指针指向 $(0, 0)$ 处。
由于 CTOX 的特殊架构,在给它编程时,也只能使用一种特殊的指令集(同样由 clevertick 设计),这种指令集包含的指令有:
x
:当开关为开时,指针向左移动 $1$ 单位,否则指针向右移动 $1$ 单位;
X
:当开关为开时,指针向左移动,否则指针向右移动,移动的单位数等于移动前指针所指内存地址处存储的值;
u
:当开关为开时,指针向上移动 $1$ 单位,否则什么也不做;
U
:当开关为开时,指针向上移动,移动的单位数等于移动前指针所指内存地址处存储的值,否则(开关为关时)什么也不做;
d
:当开关为开时,指针向下移动 $1$ 单位,否则什么也不做;
D
:当开关为开时,指针向下移动,移动的单位数等于移动前指针所指内存地址处存储的值,否则(开关为关时)什么也不做;
I
:当开关为开时,将指针所指内存地址处存储的值加 $1$(原值为 $65535$ 则加 $1$ 后变为 $0$),否则(开关为关时)什么也不做;
S
:若目前指针所指内存地址处存储的值为奇数,则将开关打开,否则将开关关上;
P
:触发一次开关(原来开则关,原来关则开);
l
:将目前指针所指内存地址处存储的值左移 $1$ 位(先转化为 $16$ 位二进制数,然后丢弃最高位,其余位顺次左移 $1$ 位,最低位补 $0$);
r
:将目前指针所指内存地址处存储的值右移 $1$ 位(先转化为 $16$ 位二进制数,然后丢弃最低位,其余位顺次右移 $1$ 位,最高位补 $0$);
v
:将目前指针所指内存地址处存储的值各位逆序(先转化为 $16$ 位二进制数,然后逆序,即 $x_{15}x_{14} \cdots x_{1}x_{0}$ 变为 $x_{0}x_{1} \cdots x_{14}x_{15}$)
E
:表示终止整个程序的运行,即一旦执行到该指令,程序立即终止,这也是唯一的能让程序终止的指令。
此外,它还包含两种特殊的括号格式:
(指令序列)数字
:表示执行括号内的指令序列指定次数,数字必须有且必须是常数,值为 $ 1 \sim 65535$,如,(u)3
表示当开关为开时,指针向上移动 $3$ 单位,否则什么也不做;
[指令序列]
:在开关为开时,不断执行括号内的指令序列,相当于 while(开关为开){指令序列}
。
CTOX 程序执行时,所有不含括号的的指令序列均严格地从左至右执行,当且仅当执行到指令 E
时,程序终止(若已经执行到程序末尾,之后已经无指令时仍未发现指令 E
,则会报错)。内存中用于存储程序的空间位于距 $(0, 0)$ 处很远(你可以认为是无限远,即程序的执行不会破坏程序本身),但这个空间不够大,所以你的程序最多只能包含 $10000$ 个字符(括号和数字也计入字符),此外,当一个程序已经执行了 $20120526$ 条指令后,仍然没有终止,则 CTOX 处理器会认为程序已经死掉,并强行终止程序。
clevertick 使用 CTOX 很快完成了他的工作。突然,某个智商较低的人发现了 CTOX 的强大之处,于是他也想来使用 CTOX。clevertick 显然不会直接同意的,于是给这个人布置了 $10$ 个任务,要求他用 CTOX 的指令集编程,完成这 $10$ 个任务。由于这个人智商低,实在无法完成任务,于是求助于你,你能帮助他么?
这 $10$ 个任务分别对应于本题的 $10$ 个测试点,任务的编号即为测试点的编号。(注:对于每个任务而言,初始时除了任务描述中说明的内存地址存有值之外,其余内存地址存储的值均为 $0$,开关为关,指针指在地址 $(0, 0)$ 处)
任务描述
- 初始时,地址 $(0, 0)$ 处存有一个值,若该值是偶数,则指针在地址 $(1, 1)$ 处终止,否则(是奇数)指针在地址 $(-1, -1)$ 处终止。终止时,任何地址处存储的值、开关状态均没有限制。
- 初始时,地址 $(0, 0)$ 处存有一个值,若该值是 $4$ 的倍数,则指针在地址 $(1, 1)$ 处停止,否则(不是 $4$ 的倍数)指针在地址 $(-1, -1)$ 处终止。终止时,任何地址处存储的值、开关状态均没有限制。
- 初始时,地址 $(0, 0)$ 处存有一个值,要求将这个值赋予地址 $(2, 3)$ 处。终止时,除地址 $(2, 3)$ 处的值外,其余地址处的值、指针位置、开关状态均没有限制。
- 初始时,地址$(1, 1)$处和$(-1, -1)$处各存有一个值,要求判断这两个值是否相等,若相等,在地址$(0, 0)$处存入$1$,若不等,在地址$(0, 0)$处存入$2$。终止时,除地址$(0, 0)$处的值外,其余地址处的值、指针位置、开关状态均没有限制。
- 初始时,地址$(1, 1)$处和$(-1, -1)$处各存有一个值,求出这两个值的按位与
AND
,并将结果存入地址$(0, 0)$处。终止时,除地址$(0, 0)$处的值外,其余地址处的值、指针位置、开关状态均没有限制。 - 初始时,地址$(1, 1)$处和$(-1, -1)$处各存有一个值,求出这两个值的按位异或
XOR
,并将结果存入地址$(0, 0)$处。终止时,除地址$(0, 0)$处的值外,其余地址处的值、指针位置、开关状态均没有限制。 - 初始时,地址$(1, 1)$处和$(-1, -1)$处各存有一个值,求出这两个值相加的和mod $65536$的值,并将结果存入地址$(0, 0)$处。终止时,除地址$(0, 0)$处的值外,其余地址处的值、指针位置、开关状态均没有限制。
- 初始时,有$64$个数依次存储在地址 $(1, 0),(2, 0) \cdots (64, 0)$ 处,其中有且仅有一个$0$,要求找到这个值为$0$的地址并终止在这里。终止时,任何地址处存储的值、开关状态均没有限制。
- 初始时,有$32768$个数依次存储在地址$(1, 0), (2, 0) \cdots (32768, 0)$处,其中有且仅有一个$0$,要求找到这个值为$0$的位置并终止在这里。终止时,任何地址处存储的值、开关状态均没有限制。
- 初始时,有$4096$个数依次存储在地址$(1, 0), (2, 0) \cdots (4096, 0)$处,求出这些值中的最大值并将其存储在地址$(0, 0)$处。终止时,除地址$(0, 0)$处的值外,其余地址处的值、指针位置、开关状态均没有限制。
本题没有输入文件,你需要对这$10$个任务,分别提交对应的输出文件 ctox1.out
~ ctox10.out
,每个输出文件一行,存放你编写的程序(若输出文件有多行,则在评测时除了第一行外,其它的行都会被舍弃)。
你如何测试自己的程序
本题提供了测试工具——模拟器 simulator
(Linux)/simulator.exe
(Windows),你可以用以下两种方式使用它。
方式一:./simulator <程序文件>
(Linux)/simulator.exe <程序文件>
(Windows)
模拟器将读取程序文件中的第一行作为程序,执行它并将程序终止时的结果写入 log.txt
中。在这种方式下,初始时任何内存地址处存储的值均为0。
方式二:./simulator <程序文件> <输入文件>
(Linux)/simulator.exe <程序文件> <输入文件>
(Windows)
其中,输入文件每行包含三个整数$x, y, v$,表示将内存地址$(x, y)$处的初始值设为$v$,必须满足 $0 \leq v \leq 65535$,若不满足则视为结束标志(后面的所有内容将被舍弃)。
模拟器将先读取输入文件中的每一行直到读取到不满足$0 \leq v\leq 65535$的行为止,并设置对应内存地址的初始值,然后读取程序文件中的第一行作为程序,执行它并将程序终止时
的结果写入log.txt
中。在这种方式下,除输入文件中指定的地址之外,初始时任何内存地址处存储的值均为$0$。
log.txt
包含程序执行步数(每条指令一步),以及程序终止时,CTOX的状态——指针位置、开关状态、所有存储的值非$0$的内存地址及其存储的值。
如果相应的文件不存在、程序出现语法错误(含有不允许的字符、括号不匹配、()后的数字不在$1$~$65535$范围内等)、长度超过$10000$个字符、执行步数超过了$20120526$、或者执行到程序末尾时未发现E
指令,模拟器会报错,此时不会生成log.txt
文件。
评分说明
在评分时,我们对每个测试点设置了$10$组数据,每组数据表示该测试点的一种初始情况(即初始时存有值的那些地址存储的值),在数据规模的设置上,保持一定的梯度。
对于每个测试点:
如果你未提交对应的输出文件,提交的输出文件中的程序有语法错误,或者程序的字符数超过了$10000$,则该测试点的所有数据均得$0$分。
否则,我们会依次以该测试点的每组数据作为输入,执行你提交的程序。对于该组数据,如果你的程序没有完成任务的要求,或者执行步数超过了$20120526$,则得$0$分,否则得$1$分。
你本题的得分为所有测试点的所有数据的得分之和。
提示
请妥善保管所有附加文件,以免误删或误改。
UOJ 上测评的特殊说明
这道题原本的 checker 和 simulator 对于指令数的计算有问题,空循环会不算任何指令数,导致测评陷入死循环。UOJ 测评时将 )
和 ]
也算作了指令的一种,但考虑到这题正确做法远远不会用到 $20120526$ 条指令,下发的 simulator 仍保持原样。
还有个问题是虽然题目中声称 “CTOX 拥有足够大(可以认为是无穷大)的内存”,原题的 checker 和 simulator 实现都是采用的 32 位的 int 存储内存地址。因此,实际上你只能在 $[-2^{31}, 2^{31} - 1] \times [-2^{31}, 2^{31} - 1]$ 的范围内移动,超出边界会自动按 int 的规则截断。由于这题正确做法并不需要用到超过 int 范围的地址,这里 UOJ 测评时不对原题的 checker 做出修改。