题解:P17242 [IOI 2026] 方块游戏 / tiling
zhao_ry514114 · · 题解
思路
问题转化
每个方块大小为
因此这个棋盘可以被划分为
所以问题变成:给定一个至少有一个白格子的
贪心
我们维护如下不变量:
对于已经放置的每一个
2\times2 方块,它朝向未填充区域的那个角一定是白色。
例如:
-
位于左上区域的方块,右下角一定为白色。
-
位于右上区域的方块,左下角一定为白色。
-
位于左下区域的方块,右上角一定为白色。
-
位于右下区域的方块,左上角一定为白色。
考虑让每个方块的白格都朝向棋盘此时的未填满区域(注意,不一定是棋盘的几何中心),使每次操作后不变量仍成立。
假设当前所有已放置的方块满足不变量。
收到一个新方块后,由于它至少有一个白格,我们按照白格的位置选择对应区域。
如果一个
具体:
| 白格位置 | 放置区域 |
|---|---|
| 左上 | 右下 |
| 右上 | 左下 |
| 左下 | 右上 |
| 右下 | 左上 |
例如我们收到:
此时白格在左上。
假如给出的棋盘大小为
此时这个
这样放置后,新方块朝向未填充区域的角就是白色,因此不变量仍然成立。
正确性证明
下面证明该不变量可以保证不存在全黑的
初始时棋盘为空,显然成立。
首先,一个全黑的
因此,如果存在全黑的
考虑它所在的位置。
第一种
任意两个
备注:图片内容不是按照正解思路画的,只是为了展示“假设存在
如图,中间出现全黑的
根据不变量:
上方方块靠近中心的一侧存在白格;下方方块靠近中心的一侧存在白格。
因此这个
第二种
四个
然而根据不变量:
左上方块的右下角是白色;右上方块的左下角是白色;左下方块的右上角是白色;右下方块的左上角是白色。
因此这个
时间复杂度
共有
由于
代码
并不长。
#include <bits/stdc++.h>
#define rep(i,j,k) for (int i = (j); i <= (k); i ++)
#define per(i,j,k) for (int i = (j); i >= (k); i --)
#define uint unsigned int
#define ll long long
#define ull unsigned long long
#define db double
#define ldb long double
#define mkp make_pair
#define eb emplace_back
#define INF = 0x3f3f3f3f;
#define LINF = 0x3f3f3f3f3f3f3f3f;
//#define MOD 998244353
//#define MOD 1000000007
//#define int ll
using namespace std;
const int MAXN = 105;
//int T;
int n, m;
bool u[MAXN][MAXN]; // 用于标记该小区域是否被使用,我是按照 2 * 2 的小区域标记的,输出需要乘 2
// 这道题是一个交互题
// 不过无需使用主函数
// 每个样例会使用你写的 init 函数 和 receive_block 函数获取数据
pair<int, int> solve (int a) {
// 直接遍历整个棋盘寻找第一个符合要求(从该方向上寻找第一个未被使用的小区域)
// 例如第一种
// 其实不一定是 i 优先,你也可以先遍历 j
// 只要保证有一个白格子指向未被覆盖的区域即可
if (a == 1) rep (i, 0, n - 1) rep (j, 0, m - 1) if (! u[i][j]) return {i, j};
if (a == 2) rep (i, 0, n - 1) per (j, m - 1, 0) if (! u[i][j]) return {i, j};
if (a == 3) per (i, n - 1, 0) rep (j, 0, m - 1) if (! u[i][j]) return {i, j};
per (i, n - 1, 0) per (j, m - 1, 0) if (! u[i][j]) return {i, j};
}
void init (int N, int M) {
n = N;
m = M;
}
pair<int, int> receive_block (int TL, int TR, int BL, int BR) {
// 由上文知,你需要根据白格的位置选择摆放位置
// 选择任意白格即可,原因已经解释过
auto z = solve (! TL ? 4 : (! TR ? 3 : (! BL ? 2 : 1)));
int x = z.first, y = z.second;
u[x][y] = true; // 标记使用
return {x * 2, y * 2}; // 这是输出!返回你找到的答案
}
如果你看到这里,我想说明一下为什么可以想到这样的做法
这道题容易想到统计黑白格数量,但由于方块出现顺序未知,任何依赖未来信息或者整体数量的策略都无法在线实现。
注意到题目唯一保证的是:每个方块至少存在一个白格。
因此我们应该利用这个必然存在的白格,主动安排它的位置,使它成为阻止全黑