简易矩形画图系统

103 阅读5分钟

简易矩形画图系统

背景介绍

设计并实现一个简易的画图程序。该程序在一个 100 * 100 的网格画布上进行操作,画布的左上角坐标为 [0, 0]

核心概念

  • 画布 (Canvas): 一个 100x100 的二维网格。
  • 矩形 (Rectangle): 由其左上角坐标 [row, col]、宽度 width 和高度 height 共同定义。它覆盖的区域是从行 rowrow + height - 1,列 colcol + width - 1

功能要求

你需要实现一个 PbrushSystem 类,支持以下操作:

PbrushSystem()
  • 功能: 初始化系统。
  • 初始状态: 画布上没有任何图形。
drawRectangle(int row, int col, int width, int height)
  • 功能: 在画布的指定位置 [row, col] 绘制一个大小为 width * height 的新矩形。

  • 失败条件:

    1. 超出边界: 新矩形的任何部分超出了 100x100 的画布范围。
    2. 出现重叠: 新矩形与画布上任何一个已存在的矩形有重叠。
  • 返回值: 绘制成功返回 true;如果因超出边界或重叠而失败,则直接返回 false

eraseArea(int row, int col, int width, int height)
  • 功能: 定义一个擦除区域 [row, col, width, height]。将画布上所有与此区域有重叠的矩形完整地擦除掉。
  • 返回值: 本次操作实际擦除的矩形总个数。
queryArea()
  • 功能: 计算一个能完全覆盖画布上所有已绘制矩形的最小矩形区域的面积(即最小包围盒的面积)。
  • 返回值: 返回该最小区域的面积。如果画布上没有任何矩形,则返回 0

输入格式

  • 函数调用总次数不超过 1000 次。
  • 0 <= row, col <= 99
  • 1 <= 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 解释

#调用解释与系统状态变化返回值
1PbrushSystem()初始化系统。当前画布为空。null
2queryArea()画布为空,没有矩形,面积为 0。0
3drawRectangle(0, 1, 5, 4)成功绘制蓝色矩形。true
4drawRectangle(0, 7, 1, 1)成功绘制绿色矩形。true
5drawRectangle(2, 7, 1, 3)成功绘制黄色矩形。true
6drawRectangle(5, 3, 2, 2)成功绘制红色矩形。true
7drawRectangle(4, 1, 3, 2)失败。新矩形 [4,1,3,2] 与已有的蓝色矩形 [0,1,5,4] 和红色矩形 [5,3,2,2] 都有重叠。false
8queryArea()当前有4个矩形。覆盖它们的最小区域从左上角 (0,1) 到右下角 (6,7)。宽=7-1+1=7, 高=6-0+1=7。面积 = 7*7=4949
9eraseArea(1, 3, 4, 6)擦除区域与蓝色矩形 [0,1,5,4] 和红色矩形 [5,3,2,2] 重叠,将这两个矩形完整擦除。2
10eraseArea(1, 3, 4, 6)再次擦除相同区域。此时已没有矩形与该区域重叠。0
11queryArea()画布上只剩下绿色和黄色矩形。最小包围盒从 (0,7)(4,7)。宽=7-7+1=1, 高=4-0+1=5。面积 = 1*5=55
12drawRectangle(2, 8, 93, 1)失败。新矩形右边界为 8+93-1 = 100,超出了画布边界(0-99)。false
13drawRectangle(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;
    }
}