题解:洛谷 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

【核心思想】

  1. 问题分析:给定 \(N \times N\) 的网格和 \(M\) 次操作,每次操作尝试在指定位置放置一个 \(2 \times 2\) 的方块。若该 \(2 \times 2\) 区域的 \(4\) 个格子均未被占用,则放置成功并标记这 \(4\) 个格子为已占用;否则跳过。求最终成功放置的方块数量。这是一个直接模拟/集合标记问题,关键在于高效判断 \(2 \times 2\) 区域是否可放置。

  2. 算法选择

    • 哈希表/映射标记:用 map<PII, int> 记录每个坐标 \((x, y)\) 是否被占用,实现 \(O(\log n)\) 的单点查询与标记
    • 贪心策略:按操作顺序依次处理,能放就放,不放则跳过,顺序不影响最终答案
  3. 关键步骤

    • 初始化:创建空映射 \(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\)
  4. 时间/空间复杂度

    • 时间复杂度:\(O(M \log M)\),每次操作进行 \(4\) 次映射查询/插入,每次 \(O(\log |mp|)\)
    • 空间复杂度:\(O(M)\),最多存储 \(4M\) 个被占用的坐标点
  5. 集合标记的核心思想

    • 点集覆盖:将 \(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
posted @ 2026-08-27 11:04  团爸讲算法  阅读(8)  评论(0)    收藏  举报