题解:洛谷 P2956 The Robot Plow
【题目来源】
洛谷:P2956 [USACO09OCT] The Robot Plow G - 洛谷
【题目描述】
农夫约翰购买了一台新的自动耕地机器人,以将自己从日复一日犁地的繁重劳动中解脱出来。这台机器人确实能完成耕地任务,但有一个小缺点:它只能犁出各边长度均为整数的完美矩形地块。
由于约翰的田地中分布着树木和其他障碍物,他需要设定机器人犁耕多个不同的矩形区域,这些区域可能会有重叠。他很好奇,在按照各种耕地指令(每个指令通过给出矩形的左下角和右上角 x、y 坐标来描述)编程后,田地里实际被犁过的方格数量究竟有多少。
和往常一样,田地被划分为若干方格,这些方格的边与 x 轴和 y 轴平行。整块田地的宽度为 \(X\) 个方格,高度为 \(Y\) 个方格 \(1\le X,Y \le 240)\)。共有 \(I\) 条耕地指令 \((1\le I \le 200)\),每条指令包含四个整数:\(Xll、Yll、Xur \text{ 和 } Yur\text{ (}1 \le Xll \le Xur \le X;1 \le Yll \le Yur \le Y)\),分别表示待犁矩形的左下角和右上角坐标。机器人会犁耕区间 \((Xll \dots Xur, Yll \dots Yur)\) 内的所有田地方格——根据不同的理解方式,这个区间可能比初始设想的多一行或一列(当然,具体取决于你如何理解)。
以一块宽 6 格、高 4 格的田地为例。当约翰发出两条耕地指令(如下所示)时,田地的犁耕情况如 * 和 # 所示(通常犁过的田地看起来相同,这里用 # 表示最近犁过的区域):
...... **.... #####.
...... (1,1)(2,4) **.... (1,3)(5,4) #####.
...... **.... **....
...... **.... **....
最终共有 14 个方格被犁过。
得分:25 分
【输入】
共 \(I+1\) 行
第一行,三个空格分隔的整数:\(X、Y\text{ 和 }I\)
第二行到第 \(I+1\) 行:第 \(i+1\) 行包含第 \(i\) 条耕地指令,由四个整数描述:\(Xll、Yll、Xur \text{ 和 } Yur\)
【输出】
一行,一个整数,表示被犁过的方格总数
【输入样例】
6 4 2
1 1 2 4
1 3 5 4
【输出样例】
14
【核心思想】
-
问题分析:给定 \(X \times Y\) 的网格和 \(I\) 个矩形犁耕指令,每个指令将指定矩形区域内的格子标记为已犁。求最终被犁过的格子总数(重叠区域只计一次)。这是一个二维差分/直接标记问题,关键在于用二维数组标记所有被覆盖的格子,最后统计标记数量。
-
算法选择:
- 直接标记:由于 \(X, Y \le 240\) 且 \(I \le 200\),直接对每个矩形区域进行双重循环标记,最后统计所有标记为 \(1\) 的格子即可
-
关键步骤:
- 读取输入:\(X, Y, I\) 和 \(I\) 条指令 \((X_{ll}, Y_{ll}, X_{ur}, Y_{ur})\)
- 标记格子:对于每条指令,双重循环遍历 \([X_{ll}, X_{ur}] \times [Y_{ll}, Y_{ur}]\),将 \(a[i][j]\) 置为 \(1\)
- 统计答案:遍历整个 \(X \times Y\) 网格,统计 \(a[i][j] = 1\) 的格子数
- 输出 \(ans\)
-
时间/空间复杂度:
- 时间复杂度:\(O(I \cdot X \cdot Y + X \cdot Y)\),每次标记最多 \(240 \times 240\),统计一次全网格
- 空间复杂度:\(O(X \cdot Y)\),标记数组
-
直接标记的核心思想:
- 小规模直接模拟:\(X, Y \le 240\) 使得直接暴力标记完全可行,无需复杂优化
- 重叠自动去重:同一个格子被多次标记仍为 \(1\),最后统计时自然只计一次
- 坐标映射:题目坐标直接对应数组下标,无需额外转换
- 适用于"小规模网格 + 矩形覆盖统计"类问题,核心是直接模拟覆盖过程
【解题思路】

【算法标签】
入门 #模拟
【代码详解】
#include <bits/stdc++.h>
using namespace std;
int x, y, I, xll, yll, xur, yur, ans=0;
int a[245][245] = {0}; // 初始化田地,缺省所有位置均为0
int main()
{
cin >> x >> y >> I; // 输入x、y和I
for (int i=1; i<=I; i++) { // 遍历I次操作
cin >> xll >> yll >> xur >> yur; // 记录下左下角和右上角的坐标(将矩阵顺时针旋转90度,则是左上角和右下角的坐标)
for (int i=xll; i<=xur; i++) { // 遍历左上角至左下角的坐标范围
for (int j=yll; j<=yur; j++) {
a[i][j] = 1; // 该范围内所有位置均赋值为1
}
}
}
for (int i=1; i<=x; i++) { // 遍历x和y范围的田地
for (int j=1; j<=y; j++) {
if (a[i][j]) ans++; // 如果当前位置为1,则表示被犁过了,ans自增1
}
}
cout << ans << endl; // 打印被犁过的的地方
return 0;
}
【运行结果】
6 4 2
1 1 2 4
1 3 5 4
14
浙公网安备 33010602011771号