题解:洛谷 AT_abc436_c 2x2 Placing
【题目来源】
洛谷:AT_abc436_c [ABC436C] 2x2 Placing - 洛谷
【题目描述】
有一个 \(N\) 行 \(N\) 列的网格。令 \((i,j)\) 表示从上到下第 \(i\) 行、从左到右第 \(j\) 列的格子。最初,网格上没有放置任何物品。
你将进行 \(M\) 次操作。第 \(i\) 次操作(\(1\leq i\leq M\))如下:
- 如果以单元格 \((R_i,C_i)\) 为左上角的 \(2\times 2\) 区域上没有任何已放置的方块,就在该区域放置一个方块。更具体地说,对于 \(S=\lbrace (R_i,C_i),(R_i+1,C_i),(R_i,C_i+1),(R_i+1,C_i+1)\rbrace\),如果这四个格子中有任意一个已经被其他方块占用,则不进行任何操作;否则,在 \(S\) 所有四个格子上放置一个方块。
请在所有操作结束后,求最终共放置了多少个方块。
【输入】
输入从标准输入读入,格式如下:
\(N\) \(M\)
\(R_1\) \(C_1\)
\(R_2\) \(C_2\)
\(\vdots\)
\(R_M\) \(C_M\)
【输出】
输出答案。
【输入样例】
4 3
1 1
2 2
2 3
【输出样例】
2
【核心思想】
-
问题分析:给定 \(N \times N\) 的网格和 \(M\) 次操作,每次操作尝试在指定位置放置一个 \(2 \times 2\) 的方块。若该 \(2 \times 2\) 区域的 \(4\) 个格子均未被占用,则放置成功并标记这 \(4\) 个格子为已占用;否则跳过。求最终成功放置的方块数量。这是一个直接模拟/集合标记问题,关键在于高效判断 \(2 \times 2\) 区域是否可放置。
-
算法选择:
- 哈希表/映射标记:用
map<PII, int>记录每个坐标 \((x, y)\) 是否被占用,实现 \(O(\log n)\) 的单点查询与标记 - 贪心策略:按操作顺序依次处理,能放就放,不放则跳过,顺序不影响最终答案
- 哈希表/映射标记:用
-
关键步骤:
- 初始化:创建空映射 \(mp\),答案计数器 \(ans = 0\)
- 逐次处理操作(遍历 \(i\) 从 \(1\) 到 \(M\),当前左上角为 \((x, y)\)):
- 占用检查:查询 \(mp[\{x, y\}]\)、\(mp[\{x, y+1\}]\)、\(mp[\{x+1, y\}]\)、\(mp[\{x+1, y+1\}]\) 是否均为 \(0\)
- 放置判断:
- 若 \(4\) 个格子均未被占用:\(ans \mathrel{+}= 1\),并将这 \(4\) 个坐标在 \(mp\) 中标记为 \(1\)
- 否则:跳过本次操作
- 输出 \(ans\)
-
时间/空间复杂度:
- 时间复杂度:\(O(M \log M)\),每次操作进行 \(4\) 次映射查询/插入,每次 \(O(\log |mp|)\)
- 空间复杂度:\(O(M)\),最多存储 \(4M\) 个被占用的坐标点
-
集合标记的核心思想:
- 点集覆盖:将 \(2 \times 2\) 方块抽象为 \(4\) 个格子的集合,放置条件转化为"集合与已占用点集无交集"
- 惰性标记:仅在放置成功时标记,未放置的位置不占用额外空间
- 顺序无关性:由于一旦占用永久占用,操作顺序不影响最终结果,贪心模拟即为最优
- 适用于"区域覆盖 + 冲突检测"类问题,核心是快速判断区域可用性
【算法标签】
普及- #模拟
【代码详解】
#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> PII; // 定义坐标对类型
const int N = 200005; // 最大点数(未使用)
int n, m; // n: 坐标范围?, m: 操作次数
int ans; // 答案:不重叠的2×2正方形数量
map<PII, int> mp; // 记录每个点是否被占用,1表示被占用
int main()
{
// 输入n和m
cin >> n >> m;
// 处理m次操作
for (int i = 1; i <= m; i++)
{
int x, y;
cin >> x >> y; // 输入2×2正方形的左上角坐标
// 检查以(x,y)为左上角的2×2正方形是否与已存在的正方形重叠
// 一个2×2正方形包含4个点:(x,y), (x,y+1), (x+1,y), (x+1,y+1)
// 如果这4个点都没有被占用,说明可以放置新的正方形
if (mp[{x, y}] != 1 && // 左上角
mp[{x, y + 1}] != 1 && // 右上角
mp[{x + 1, y}] != 1 && // 左下角
mp[{x + 1, y + 1}] != 1) // 右下角
{
// 可以放置新正方形
ans++; // 增加计数
// 标记这4个点为已占用
mp[{x, y}] = 1;
mp[{x, y + 1}] = 1;
mp[{x + 1, y}] = 1;
mp[{x + 1, y + 1}] = 1;
// 注意:这里没有检查坐标是否越界
// 假设输入的坐标都在有效范围内
}
}
// 输出不重叠的正方形数量
cout << ans << endl;
return 0;
}
【运行结果】
4 3
1 1
2 2
2 3
2
浙公网安备 33010602011771号