简易矩形画图系统
背景介绍
设计并实现一个简易的画图程序。该程序在一个 100 * 100 的网格画布上进行操作,画布的左上角坐标为 [0, 0]。
核心概念
- 画布 (Canvas): 一个 100x100 的二维网格。
- 矩形 (Rectangle): 由其左上角坐标
[row, col]、宽度width和高度height共同定义。它覆盖的区域是从行row到row + height - 1,列col到col + width - 1。
功能要求
你需要实现一个 PbrushSystem 类,支持以下操作:
PbrushSystem()
- 功能: 初始化系统。
- 初始状态: 画布上没有任何图形。
drawRectangle(int row, int col, int width, int height)
-
功能: 在画布的指定位置
[row, col]绘制一个大小为width * height的新矩形。 -
失败条件:
- 超出边界: 新矩形的任何部分超出了 100x100 的画布范围。
- 出现重叠: 新矩形与画布上任何一个已存在的矩形有重叠。
-
返回值: 绘制成功返回
true;如果因超出边界或重叠而失败,则直接返回false。
eraseArea(int row, int col, int width, int height)
- 功能: 定义一个擦除区域
[row, col, width, height]。将画布上所有与此区域有重叠的矩形完整地擦除掉。 - 返回值: 本次操作实际擦除的矩形总个数。
queryArea()
- 功能: 计算一个能完全覆盖画布上所有已绘制矩形的最小矩形区域的面积(即最小包围盒的面积)。
- 返回值: 返回该最小区域的面积。如果画布上没有任何矩形,则返回
0。
输入格式
- 函数调用总次数不超过 1000 次。
0 <= row, col <= 991 <= width, height <= 100
输出格式
- 根据每个函数的原型要求返回相应的值。最终的整体输出由评测框架完成。
样例
输入样例 1
["PbrushSystem", "queryArea", "drawRectangle", "drawRectangle", "drawRectangle", "drawRectangle", "drawRectangle", "queryArea", "eraseArea", "eraseArea", "queryArea", "drawRectangle", "drawRectangle"]
[[], [], [0, 1, 5, 4], [0, 7, 1, 1], [2, 7, 1, 3], [5, 3, 2, 2], [4, 1, 3, 2], [], [1, 3, 4, 6], [1, 3, 4, 6], [], [2, 8, 93, 1], [2, 8, 92, 1]]
输出样例 1
[null, 0, true, true, true, true, false, 49, 2, 0, 5, false, true]
样例 1 解释
| # | 调用 | 解释与系统状态变化 | 返回值 |
|---|---|---|---|
| 1 | PbrushSystem() | 初始化系统。当前画布为空。 | null |
| 2 | queryArea() | 画布为空,没有矩形,面积为 0。 | 0 |
| 3 | drawRectangle(0, 1, 5, 4) | 成功绘制蓝色矩形。 | true |
| 4 | drawRectangle(0, 7, 1, 1) | 成功绘制绿色矩形。 | true |
| 5 | drawRectangle(2, 7, 1, 3) | 成功绘制黄色矩形。 | true |
| 6 | drawRectangle(5, 3, 2, 2) | 成功绘制红色矩形。 | true |
| 7 | drawRectangle(4, 1, 3, 2) | 失败。新矩形 [4,1,3,2] 与已有的蓝色矩形 [0,1,5,4] 和红色矩形 [5,3,2,2] 都有重叠。 | false |
| 8 | queryArea() | 当前有4个矩形。覆盖它们的最小区域从左上角 (0,1) 到右下角 (6,7)。宽=7-1+1=7, 高=6-0+1=7。面积 = 7*7=49。 | 49 |
| 9 | eraseArea(1, 3, 4, 6) | 擦除区域与蓝色矩形 [0,1,5,4] 和红色矩形 [5,3,2,2] 重叠,将这两个矩形完整擦除。 | 2 |
| 10 | eraseArea(1, 3, 4, 6) | 再次擦除相同区域。此时已没有矩形与该区域重叠。 | 0 |
| 11 | queryArea() | 画布上只剩下绿色和黄色矩形。最小包围盒从 (0,7) 到 (4,7)。宽=7-7+1=1, 高=4-0+1=5。面积 = 1*5=5。 | 5 |
| 12 | drawRectangle(2, 8, 93, 1) | 失败。新矩形右边界为 8+93-1 = 100,超出了画布边界(0-99)。 | false |
| 13 | drawRectangle(2, 8, 92, 1) | 成功。新矩形右边界为 8+92-1 = 99,未超出边界。 | true |
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class PbrushSystem {
// 使用一个列表来存储所有已绘制的矩形
private List<Rectangle> drawnRects;
private static final int CANVAS_SIZE = 100;
/**
* 内部静态类,用于表示一个矩形,让代码更清晰
*/
private static class Rectangle {
int row, col, width, height;
public Rectangle(int row, int col, int width, int height) {
this.row = row;
this.col = col;
this.width = width;
this.height = height;
}
}
/**
* 辅助函数:判断两个矩形是否重叠
* @param r1 第一个矩形
* @param r2 第二个矩形
* @return 如果重叠返回 true,否则返回 false
*/
private static boolean rectanglesOverlap(Rectangle r1, Rectangle r2) {
// 反向逻辑:检查所有不重叠的可能性
// r1在r2的左边
if (r1.col + r1.width <= r2.col) return false;
// r1在r2的右边
if (r1.col >= r2.col + r2.width) return false;
// r1在r2的上边
if (r1.row + r1.height <= r2.row) return false;
// r1在r2的下边
if (r1.row >= r2.row + r2.height) return false;
// 如果以上都不是,则一定重叠
return true;
}
/**
* PbrushSystem() — 初始化系统
*/
public PbrushSystem() {
// 初始化存储矩形的列表
this.drawnRects = new ArrayList<>();
}
/**
* drawRectangle(int row, int col, int width, int height)
* 在位置 [row, col] 绘制一个大小为 width * height 的矩形
*/
public boolean drawRectangle(int row, int col, int width, int height) {
Rectangle newRect = new Rectangle(row, col, width, height);
// 1. 检查是否超出边界
if (newRect.row + newRect.height > CANVAS_SIZE || newRect.col + newRect.width > CANVAS_SIZE) {
return false;
}
// 2. 检查是否与已有矩形重叠
for (Rectangle existingRect : this.drawnRects) {
if (rectanglesOverlap(newRect, existingRect)) {
return false;
}
}
// 3. 检查通过,添加矩形并返回成功
this.drawnRects.add(newRect);
return true;
}
/**
* eraseArea(int row, int col, int width, int height)
* 选中所有与区域 [row, col, width, height] 有重叠的矩形完整擦除
*/
public int eraseArea(int row, int col, int width, int height) {
Rectangle eraseRegion = new Rectangle(row, col, width, height);
int erasedCount = 0;
// 使用迭代器安全地删除列表元素
Iterator<Rectangle> iterator = this.drawnRects.iterator();
while (iterator.hasNext()) {
Rectangle existingRect = iterator.next();
if (rectanglesOverlap(eraseRegion, existingRect)) {
iterator.remove(); // 安全删除
erasedCount++;
}
}
return erasedCount;
}
/**
* queryArea()
* 计算完全覆盖所有矩形的最小矩形区域的面积
*/
public long queryArea() {
// 如果没有矩形,面积为0
if (this.drawnRects.isEmpty()) {
return 0;
}
// 初始化最小包围盒的边界
int minRow = CANVAS_SIZE;
int minCol = CANVAS_SIZE;
int maxRight = 0;
int maxBottom = 0;
// 遍历所有矩形,更新包围盒的四个边界
for (Rectangle rect : this.drawnRects) {
minRow = Math.min(minRow, rect.row);
minCol = Math.min(minCol, rect.col);
maxRight = Math.max(maxRight, rect.col + rect.width);
maxBottom = Math.max(maxBottom, rect.row + rect.height);
}
// 计算包围盒的宽高并返回面积
// 使用 long 防止乘法溢出,虽然本题100*100不会,但是好习惯
long boundingWidth = maxRight - minCol;
long boundingHeight = maxBottom - minRow;
return boundingWidth * boundingHeight;
}
}