我们考虑一个双人游戏。玩家们获得一个 $x \times y$ 的矩形(其中 $x$ 和 $y$ 是正整数)。 玩家轮流进行操作。一次操作包括通过单次垂直或水平切割将一个矩形分成两个矩形。得到的矩形必须具有正整数尺寸。
一个 $4 \times 3$ 矩形的所有可能切割方式。
每次切割后,较小的矩形(即面积较小的那个)会被丢弃,另一个矩形会被传给另一个玩家。如果矩形被等分成两半,则丢弃其中一半。收到 $1 \times 1$ 矩形因而无法进行操作的玩家输掉游戏。
你的任务是编写一个程序来玩这个矩形游戏并获胜。程序必须使用一个特殊的库来玩游戏。该库为你提供了 dimension_x() 和 dimension_y() 函数,它们返回矩形的尺寸。矩形的初始尺寸是介于 $1$ 到 $100\,000\,000$ 之间的整数。至少有一个维度大于 $1$。此外,在 50% 的测试用例中,尺寸不超过 $25$。
还有一个过程 cut(dir, position),由你的程序调用以进行操作。参数 dir 和 position 分别描述切割的方向和位置。参数 dir 必须是以下两个值之一:vertical 或 horizontal。如果 dir = vertical,则切割是垂直的,参数 position 指定切割的 $x$ 坐标(见上图),你必须确保 $1 \le \text{position} \le \text{dimension\_x()} - 1$。如果 dir = horizontal,则切割是水平的,参数 position 指定切割的 $y$ 坐标,你必须确保 $1 \le \text{position} \le \text{dimension\_y()} - 1$。
当你的程序启动时,它将作为其中一名玩家参与一局游戏。你的程序先手——它必须对初始矩形进行切割。当你的程序调用 cut 过程时,你的操作会被记录,控制权将传递给程序的对手。在对手操作后,控制权返回给你的程序。dimension_x() 和 dimension_y() 返回的值将反映你和对手操作后的结果。一旦你的程序获胜、失败或进行了非法操作(即使用无效参数调用 cut 过程),它将被终止。终止你的程序是一个自动过程,因此只要有可能,你的程序就应该继续进行操作。你可以假设对于测试数据,你的程序总是存在必胜策略。
你的程序不得读写任何文件,不得使用标准输入/输出,且不得尝试修改程序之外的任何内存。违反这些规则中的任何一条都可能导致取消资格。
实验
为了让你对该库进行实验,我们提供了示例对手库:它们的源文件在 preclib.pas、creclib.c 和 creclib.h 中。该库可以从 http://contest/ 下载。它们实现了一种非常简单的策略。当你运行程序时,它将与这些简单的玩家进行对战。你可以随意修改它们,并针对更好的对手测试你的程序。然而,在评测期间,你的程序将与不同的对手进行对战。
当你使用 TEST 接口提交程序时,它将与未修改的示例对手库一起编译。提交的输入文件将提供给程序的标准输入。输入文件应包含两行,每行包含一个整数。第一行应包含矩形的初始宽度,第二行应包含矩形的初始高度。这些尺寸由示例对手库读取。
如果你修改了 preclib.pas 库的实现部分,请使用以下命令重新编译它:ppc386 -O2 preclib.pas。该命令生成 preclib.o 和 preclib.ppu 文件。编译你的程序需要这些文件,并且它们应该放在你的程序所在的目录中。请不要修改 preclib.pas 库的接口部分。
如果你修改了 creclib.c 库,请记住将其(连同 creclib.h)放在你的程序所在的目录中——编译程序需要它们。请不要修改 creclib.h 文件。
还为你提供了两个简单的程序,说明上述库的使用方法:crec.c 和 prec.pas。(请记住,这些程序不是正确的解决方案。)你可以使用以下命令编译它们:
gcc -O2 -static crec.c creclib.c -lm g++ -O2 -static crec.c creclib.c -lm ppc386 -O2 -XS prec.pas
库
你获得了一个提供以下功能的库:
- FreePascal 库 (
preclib.ppu,preclib.o)
type direction = (vertical, horizontal);
function dimension_x(): longint;
function dimension_y(): longint;
procedure cut(dir: direction; position: longint);
在你的源文件 rec.pas 中包含以下语句:
uses preclib;
要编译你的程序,请将文件 preclib.o 和 preclib.ppu 复制到源文件所在的目录,并运行以下命令:
ppc386 -O2 -XS rec.pas
文件 prec.pas 给出了如何使用 preclib 库的示例。
- GNU C/C++ 库 (
creclib.h,creclib.c)
typedef enum __direction {vertical, horizontal} direction;
int dimension_x();
int dimension_y();
void cut(direction dir, int position);
在你的源文件(rec.c 或 rec.cpp)中包含以下语句:
#include "creclib.h"
要编译你的程序,请将文件 creclib.c 和 creclib.h 复制到源文件所在的目录,并运行以下命令:
gcc -O2 -static rec.c creclib.c -lm
或:
g++ -O2 -static rec.cpp creclib.c -lm
文件 crec.c 给出了如何在 C 中使用该库的示例。
样例交互
以下是你的程序与评测库之间的样例交互。它展示了一局样例游戏是如何进行的。游戏从一个 $4 \times 3$ 的棋盘开始。对于这个状态,存在必胜策略。
| 你的程序调用 | 发生的事情 |
|---|---|
dimension_x() |
返回 4 |
dimension_y() |
返回 3 |
cut(vertical, 1) |
记录你的切割,并将一个 $3 \times 3$ 的棋盘传递给你的对手,对手将其切割为 $3 \times 2$ 的棋盘;在此之后,控制权返回给你的程序 |
dimension_x() |
返回 3 |
dimension_y() |
返回 2 |
cut(horizontal, 1) |
记录你的切割,并将一个 $3 \times 1$ 的棋盘传递给你的对手,对手将其切割为 $2 \times 1$ 的棋盘;在此之后,控制权返回给你的程序 |
dimension_x() |
返回 2 |
dimension_y() |
返回 1 |
cut(vertical, 1) |
你的切割产生了一个 $1 \times 1$ 的棋盘,因此你获胜;你的程序自动终止 |