你需要设计一个算法,用于一台内存仅为 10240 位(bits)的古老计算机。内存初始化为零,之后你可以一次写入或读取一个位。
你必须在这台计算机上实现两个操作:
remember(a):$a$ 是一个 $0$ 到 $4095$ 之间的整数。
该操作的实现可以调用:
bit_set(address):$address$ 是一个 $1$ 到 $10240$ 之间的整数。
位置为 $address$ 的内存位将被设置为 $1$。
compare(b):$b$ 是一个 $0$ 到 $4095$ 之间的整数。
如果 $b < a$,应返回 $-1$。
如果 $b = a$,应返回 $0$。
如果 $b > a$,应返回 $1$。
该操作的实现可以调用:
bit_get(address)
返回位置为 $address$ 的内存位:如果该位在 remember(a) 操作期间通过 bit_set() 被设置过,则返回 $1$,否则返回 $0$。
任务
实现 remember() 和 compare(),以最小化在所有可能的 $a$ 和 $b$ 取值下,最坏情况下的总内存访问次数(bit_set() 和 bit_get() 的调用次数)。
你的程序将进行详尽的评估:
定义 AllMemory = array [0..4095][1..10240] of bits
将 AllMemory 初始化为零
对于 $a = 0..4095$:
定义 bit_set(address):AllMemory[a][address] = 1
remember(a)
令 maxA 为任意 $a$ 执行的 bit_set() 调用次数的最大值
对于 $(a,b) \in \{0..4095\} \times \{0..4095\}$ 以随机顺序(即考虑所有有效对 $(a,b)$,顺序随机)
定义 bit_get(address):返回 AllMemory[a][address]
answer = compare(b)
如果比较 $a$ 和 $b$ 的 answer 不正确:你的得分为 $0$;退出
令 maxB 为任意 $(a,b)$ 对执行的 bit_get() 调用次数的最大值
$T = maxA + maxB$
如果 $T > 20$:你的得分为 $0$;退出
否则你的得分为 $1 + 9 * (21 - T)$;退出
实现细节
C 语言实现
你的源文件必须以以下内容开头:
#include "cmp.h"
内存访问函数的原型为:
void bit_set(int addr);
int bit_get(int addr);
你必须实现以下函数:
void remember(int value);
int compare(int value);
文件 cmp.h 将提供 main() 函数,你不得定义另一个此类函数。除 bit_set() 和 bit_get() 外,所有全局名称(变量和函数)都将以 boi 为前缀。因此,你可以通过不使用此名称前缀来避免命名冲突导致的编译错误。
我们提供了 cmp.h 的示例实现文件 public-cmp.h 以供参考,你可以根据需要进行编辑。该实现与评分时使用的 cmp.h 版本不同,但程序的接口保持不变。
要编译所有代码,只需编译你的源文件,并确保 cmp.h 在同一目录下。
Pascal 语言实现
你必须在 cmp.pas 文件中实现一个名为 cmp 的单元。你的程序将使用 cmpdata 单元中定义的 bit_set() 过程和 bit_get() 函数,其原型如下:
procedure bit_set(addr:integer);
function bit_get(addr:integer):integer;
你的单元必须实现 remember() 过程和 compare() 函数,它们将被主程序调用。
procedure remember(value:integer);
function compare(value:integer):integer;
主程序将在评估期间由科学委员会提供。为了方便起见,我们提供了 public-cmp.pas 文件中的示例实现(你可以根据需要进行编辑)。这与评估期间使用的实现不同。如果你将 cmpdata.pas、cmp.pas 和 public-cmp.pas 放在同一目录下,你可以使用以下命令编译所有代码:
fpc public-cmp.pas
我们提供的代码(cmpdata.pas 和主程序)仅声明了以 boi 为前缀的变量。你不得尝试访问这些变量,为了防止名称冲突和编译错误,你应该避免在自己的变量和函数中使用 boi 前缀。
数据范围
- 任何试图与评分代码的内存进行交互的行为都可能导致你被取消资格。
- 如果你的解决方案不遵守上述协议(例如在
compare()期间调用bit_set()或使用无效地址),你将获得零分。 - 时间限制:你的实现必须在 10 秒内执行 4096 次
remember()调用和 $4096 \times 4096$ 次compare()调用。