题解:洛谷 P5729 工艺品制作
【题目来源】
【题目描述】
现有一个长宽高分别为 \(w,x,h\) 组成的实心玻璃立方体,可以认为是由 \(1\times 1\times 1\) 的数个小方块组成的,每个小方块都有一个坐标 \((i,j,k)\)。现在需要进行 \(q\) 次切割。每次切割给出 \((x_1,y_1,z_1),(x_2,y_2,z_2)\) 这 \(6\) 个参数,保证 \(x1\le x2,y1\le y2,z1\le z2\);每次切割时,使用激光工具切出一个立方体空洞,空洞的壁平行于立方体的面,空洞的对角点就是给出的切割参数的两个点。
换句话说,所有满足 \(x_1\le i\le x_2,y_1\le j\le y_2,z_1\le k\le z_2\) 的小方块 \((i,j,k)\) 的点都会被激光蒸发。例如有一个 \(4\times 4\times 4\) 的大方块,其体积为 \(64\);给出参数 $ (1,1,1),(2,2,2)$ 时,中间的 \(8\) 块小方块就会被蒸发,剩下 \(56\) 个小方块。现在想知道经过所有切割操作后,剩下的工艺品还剩下多少格小方块的体积?
【输入】
第一行三个正整数 $ w,x,h$。
第二行一个正整数 \(q\)。
接下来 \(q\) 行,每行六个整数 \((x_1,y_1,z_1),(x_2,y_2,z_2)\)。
【输出】
输出一个整数表示答案。
【输入样例】
4 4 4
1
1 1 1 2 2 2
【输出样例】
56
【核心思想】
-
问题分析:给定一个 \(w \times x \times h\) 的实心立方体(由 \(1 \times 1 \times 1\) 的小方块组成),进行 \(q\) 次切割操作。每次切割给定一个长方体空洞的对角点 \((x_1,y_1,z_1)\) 和 \((x_2,y_2,z_2)\),会移除所有满足 \(x_1 \le i \le x_2\)、\(y_1 \le j \le y_2\)、\(z_1 \le k \le z_2\) 的小方块。求最终剩余的小方块数量。本质是三维空间中的区间标记与统计问题,由于坐标范围通常较小(如 \(\le 20\)),可以直接使用三维布尔数组模拟。
-
算法选择:
- 三维数组标记:使用一个三维数组
a[25][25][25](初始全 0)表示小方块是否存在。对于每次切割,用三重循环将对应长方体区域内所有小方块标记为 1(表示已移除)。最后遍历整个立方体,统计仍为 0 的格子数量。 - 复杂度:假设立方体边长为 \(L\),每次切割的标记复杂度为 \(O(L^3)\),总复杂度 \(O(q \cdot L^3)\)。由于 \(L \le 20\),\(q\) 通常较小,完全可行。
- 三维数组标记:使用一个三维数组
-
关键步骤:
- 读入与初始化:读取 \(w, x, h\),将三维数组
a全部初始化为 0。 - 处理每次切割:读取 \(x_1, y_1, z_1, x_2, y_2, z_2\),使用三重循环遍历 \(i \in [x_1, x_2]\),\(j \in [y_1, y_2]\),\(k \in [z_1, z_2]\),将
a[i][j][k]置为 1。 - 统计剩余:遍历 \(i \in [1, w]\),\(j \in [1, x]\),\(k \in [1, h]\),统计
a[i][j][k] == 0的数量,即为剩余小方块数。 - 输出答案。
- 读入与初始化:读取 \(w, x, h\),将三维数组
-
时间/空间复杂度:
- 时间复杂度:\(O(q \cdot w \cdot x \cdot h)\),\(w, x, h \le 20\) 时非常快。
- 空间复杂度:\(O(w \cdot x \cdot h)\)。
-
三维模拟与标记:
- 空间模拟:用三维数组直接表示物理空间,是最直观的模拟方法,适用于小规模数据。
- 区间移除:每次切割等价于将一个轴对齐的长方体区域内的所有点标记为“已移除”,无需处理重叠或边界特殊情况。
- 最终统计:只需遍历整个空间统计未被标记的点数,无需考虑被移除点的形状或位置。
- 适用场景:这种“三维空间区域标记与统计”问题常见于计算几何或图形学模拟,当坐标范围很小时,暴力枚举是最可靠的方法。
【算法标签】
入门 #模拟
【代码详解】
#include <bits/stdc++.h> // 包含标准库头文件(万能头文件)
using namespace std; // 使用标准命名空间
/**
* 主函数 - 程序入口
* @return 程序执行状态码(0表示成功)
*/
int main()
{
// 定义三维数组并初始化为0,用于标记立方体区域
int a[25][25][25] = {0};
// 定义变量:
int x, y, z; // 立方体的长、宽、高
int x1, x2, y1, y2, z1, z2; // 标记区域的边界坐标
int q; // 操作次数
int ans = 0; // 未被标记的单元数量
// 输入立方体的尺寸
cin >> x >> y >> z;
// 输入操作次数
cin >> q;
// 处理每个标记操作
for (int t = 1; t <= q; t++)
{
// 输入当前标记区域的边界坐标
cin >> x1 >> y1 >> z1 >> x2 >> y2 >> z2;
// 遍历标记区域内的所有单元
for (int i = x1; i <= x2; i++)
{
for (int j = y1; j <= y2; j++)
{
for (int k = z1; k <= z2; k++)
{
a[i][j][k] = 1; // 标记当前单元
}
}
}
}
// 统计未被标记的单元数量
for (int i = 1; i <= x; i++)
{
for (int j = 1; j <= y; j++)
{
for (int k = 1; k <= z; k++)
{
if (a[i][j][k] == 0)
ans++; // 未被标记则计数
}
}
}
// 输出未被标记的单元总数
cout << ans;
return 0; // 程序正常结束
}
【运行结果】
4 4 4
1
1 1 1 2 2 2
56
浙公网安备 33010602011771号