题解:洛谷 P5729 工艺品制作

【题目来源】

洛谷:P5729 【深基5.例7】工艺品制作 - 洛谷

【题目描述】

现有一个长宽高分别为 \(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

【核心思想】

  1. 问题分析:给定一个 \(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\)),可以直接使用三维布尔数组模拟。

  2. 算法选择

    • 三维数组标记:使用一个三维数组 a[25][25][25](初始全 0)表示小方块是否存在。对于每次切割,用三重循环将对应长方体区域内所有小方块标记为 1(表示已移除)。最后遍历整个立方体,统计仍为 0 的格子数量。
    • 复杂度:假设立方体边长为 \(L\),每次切割的标记复杂度为 \(O(L^3)\),总复杂度 \(O(q \cdot L^3)\)。由于 \(L \le 20\)\(q\) 通常较小,完全可行。
  3. 关键步骤

    • 读入与初始化:读取 \(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 的数量,即为剩余小方块数。
    • 输出答案
  4. 时间/空间复杂度

    • 时间复杂度:\(O(q \cdot w \cdot x \cdot h)\)\(w, x, h \le 20\) 时非常快。
    • 空间复杂度:\(O(w \cdot x \cdot h)\)
  5. 三维模拟与标记

    • 空间模拟:用三维数组直接表示物理空间,是最直观的模拟方法,适用于小规模数据。
    • 区间移除:每次切割等价于将一个轴对齐的长方体区域内的所有点标记为“已移除”,无需处理重叠或边界特殊情况。
    • 最终统计:只需遍历整个空间统计未被标记的点数,无需考虑被移除点的形状或位置。
    • 适用场景:这种“三维空间区域标记与统计”问题常见于计算几何或图形学模拟,当坐标范围很小时,暴力枚举是最可靠的方法。

【算法标签】

入门 #模拟

【代码详解】

#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
posted @ 2026-08-30 09:16  团爸讲算法  阅读(12)  评论(0)    收藏  举报