华为OD机试真题-矩形绘制

题目描述

实现一个简单的绘图模块,绘图模块仅支持矩形的绘制和擦除

  • 当新绘制的矩形与之前的图形重善时,对图形取并集

  • 当新擦除的矩形与之前的图形重善时,对图形取差集

给定一系列矩形的绘制和擦除操作,计算最终图形的面积。下面给出示例1和示例2的图示

示例1

在这里插入图片描述

两步绘制的矩形如左侧所示,取并集后得到的图形如右侧所示

示例2
在这里插入图片描述

第一步绘制的矩形在左侧用实线表示,第二步擦除的矩形在左侧用虚线表示,取差集后得到图像如右侧所示

输入描述

绘图模块采用二维 坐标系只,输入第一行位操作的数量 N,接下来的 N 行格式为:

  • d x1 y1 x2 y2,d表示进行绘制操作,(x1,y1)为矩形左上角坐标,(x2,y2)为矩形右下角坐标
  • e x1 y1 x2 y2,e表示进行擦除操作,(x1,y1)为矩形左上角坐标,(x2,y2)为矩形右下角坐标坐

标为整数,且数据范围为[-100,100],用例保证坐标有效

输出描述

输出最终图形的面积

示例1

输入

2
d 0 2 2 0
d -1 1 1 -1

输出

7

说明

示例2

输入

2
d 0 2 2 0
e -1 1 1 -1

输出

3

题解

把每次的矩形分解称为一个个的小方格,方格的数量即为面积的总和

源码 Java

import java.util.HashSet;

public class DrawArea {

	static Input input;
	static {
		input = new Input("2\n" +
				"d 0 2 2 0\n" +
				"d -1 1 1 -1");
		input = new Input("2\n" +
				"d 0 2 2 0\n" +
				"e -1 1 1 -1");
	}

	public static void main(String[] args) {
		Integer count = Integer.parseInt(input.nextLine());
		HashSet<String> set = new HashSet<>();
		for (int i = 0; i < count; i++) {
			String[] ss = input.nextLine().split(" ");
			String type = ss[0];
			int x1 = Integer.parseInt(ss[1]);
			int y1 = Integer.parseInt(ss[2]);
			int x2 = Integer.parseInt(ss[3]);
			int y2 = Integer.parseInt(ss[4]);
			for (int x = x1; x < x2; x++) {
				for (int y = y1; y > y2; y--) {
					String string = new StringBuilder().append(x).append(y).append(x + 1).append(y - 1).toString();
					if ("d".equals(type)) {
						set.add(string);
					}
					if ("e".equals(type)) {
						set.remove(string);
					}
				}
			}
		}
		System.out.println(set.size());
	}
}
### 实现矩形绘制的功能 为了实现华为OD环境下完成矩形绘制和擦除的操作,可以基于参考中的描述构建一个解决方案。以下是完整的代码实现以及详细的解释。 #### 总体设计 程序的核心逻辑在于维护当前的图形状态,并根据输入指令动态更新该状态。对于每次操作: - **绘制(d)**:将新的矩形区域加入到现有图形中,形成并集。 - **擦除(e)**:从现有的图形中移除指定的矩形区域,形成差集。 最终目标是计算剩余图形的总面积。 --- ### 代码实现 (C++) ```cpp #include <bits/stdc++.h> using namespace std; // 定义扫描线算法辅助结构 struct Event { int x, type, y1, y2; }; bool cmp(const Event &a, const Event &b) { if (a.x != b.x) return a.x < b.x; return a.type < b.type; // 先处理进入事件再处理离开事件 } int main() { ios::sync_with_stdio(false); cin.tie(0); int N; cin >> N; vector<Event> events; while (N--) { char op; int x1, y1, x2, y2; cin >> op >> x1 >> y1 >> x2 >> y2; if (op == 'd') { // 绘制操作 events.push_back({x1, 1, y1, y2}); events.push_back({x2, -1, y1, y2}); } else if (op == 'e') { // 擦除操作 events.push_back({x1, -1, y1, y2}); events.push_back({x2, 1, y1, y2}); } } sort(events.begin(), events.end(), cmp); long long total_area = 0; int current_y = -1, prev_x = -1; multiset<int> active_intervals; for (const auto &event : events) { if (!active_intervals.empty()) { int max_y = *prev(active_intervals.end()); int min_y = *active_intervals.begin(); total_area += (long long)(max_y - min_y) * (event.x - prev_x); // 计算面积增量 } if (current_y >= event.y1 && current_y <= event.y2) continue; // 跳过重复覆盖部分 if (event.type == 1) { active_intervals.insert(event.y1); active_intervals.insert(event.y2); } else if (event.type == -1) { active_intervals.erase(active_intervals.find(event.y1)); active_intervals.erase(active_intervals.find(event.y2)); } prev_x = event.x; } cout << total_area << endl; return 0; } ``` --- ### 功能说明 上述代码实现了以下功能: 1. **数据读取与预处理** 输入操作被解析为 `Event` 结构,其中每条记录包含四个字段:`x`, `type`, `y1`, 和 `y2`。`type=1` 表示矩形左侧边界(即新增),而 `type=-1` 则表示右侧边界(即结束)。这些事件按照 `x` 坐标排序以便后续扫描线算法执行[^1]。 2. **扫描线算法核心逻辑** 使用多集合 (`multiset`) 来存储当前活跃区间的所有上下界。每当遇到一个新的垂直切片时,都会重新评估当前活动区间的高度范围,并累加对应的宽度乘以高度作为面积的一部分[^3]。 3. **结果输出** 遍历完成后,累计得到的结果即是最终图形的总表面积。 --- ### 复杂度分析 - 时间复杂度:主要由两部分构成——首先是事件列表的排序过程 \(O(N \log N)\),其次是遍历过程中对多重集合的操作 \(O(\log M)\),因此整体时间复杂度约为 \(O(N \log N + Q \log M)\)[^2]。 - 空间复杂度:由于需要保存所有的事件及其关联的高度信息,空间需求大致为 \(O(N)\)。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

坚定的小辣鸡在努力

你的支持是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值