UVa 12171 Sculpture 雕塑 离散化+BFS

题目链接;Sculpture
题目描述:

给定一些长方体(这些长方体可能会出现相交),求这些长方体组成的图形的体积和表面积。

题解:

这里我们需要假设所有的长方体都是放在一个封闭的空间中的(由于坐标的范围是 [ 1 , 1000 ] [1,1000] [1,1000],我们可以设置这个封闭的空间为 ( 0 , 0 , 0 ) (0,0,0) (0,0,0) ( 1001 , 1001 , 1001 ) (1001,1001,1001) (1001,1001,1001)围成的正方体空间)然后将被占用的方块做标记,然后进行 B F S BFS BFS即可,但是这样需要遍历 1001 ∗ 1001 ∗ 1001 ≈ 1 0 9 1001*1001*1001\approx10^9 100110011001109次,这是肯定要超时的。刘汝佳在紫书中提到了需要先进性离散化在进行染色。这里的离散化实际上就是记录去重后各个坐标轴上分别有多少个点,之后根据原有的坐标在去重后的数组中找到覆盖的范围,进行标记,在进行 f l o o d f i l l floodfill floodfill的时候,入队的永远都是没有被标记的结点,当向外延伸的时候,如果到达了一个标记的结点,那么此时就可以计算表面积,而每次出队的时候,此时可以计算空气的体积。 B F S BFS BFS中计算表面积和体积的部分较难理解,需要在脑海中想象整个 f l o o d f i l l floodfill floodfill的扩充过程才能更好的理解。

代码:

#include <bits/stdc++.h>

const int MAXN = 50;
const int DIRECTION_NUM = 6;

using namespace std;

int T, n;
int x[MAXN * 2 + 2], y[MAXN * 2 + 2], z[MAXN * 2 + 2];
int color[MAXN * 2 + 2][MAXN * 2 + 2][MAXN * 2 + 2];
int lenx, leny, lenz;
int dx[] = {1, 0, 0, 0, 0, -1};
int dy[] = {0, 1, 0, 0, -1, 0};
int dz[] = {0, 0, 1, -1, 0, 0}; // 上下左右前后六个方向的坐标改变量

struct Point
{
    int x, y, z;
    Point() {}
    Point(int x, int y, int z) : x(x), y(y), z(z) {}
};

struct Cube
{
    Point p1; // 左下角坐标
    Point p2; // 右下角坐标
    Cube() {}
    Cube(int a1, int b1, int c1,
         int a2, int b2, int c2) : p1(a1, b1, c1), p2(a2, b2, c2) {}
}cube[MAXN];

void discretization()
{
    sort(x, x + 2 * n + 2);
    sort(y, y + 2 * n + 2);
    sort(z, z + 2 * n + 2);
    lenx = unique(x, x + 2 * n + 2) - x;
    leny = unique(y, y + 2 * n + 2) - y;
    lenz = unique(z, z + 2 * n + 2) - z;
}

int getPosition(int *a, int len, int val)
{
    return lower_bound(a, a + len, val) - a;
}

bool outBound(Point p)
{
    return p.x < 0 || p.y < 0 || p.z < 0 || p.x >= lenx - 1 || p.y >= leny - 1 || p.z >= lenz - 1;
}

void floodFill()
{
    memset(color, 0, sizeof(color));
    for (int i = 0; i < n; i++) {
        int lx = getPosition(x, lenx, cube[i].p1.x), rx = getPosition(x, lenx, cube[i].p2.x);
        int ly = getPosition(y, leny, cube[i].p1.y), ry = getPosition(y, leny, cube[i].p2.y);
        int lz = getPosition(z, lenz, cube[i].p1.z), rz = getPosition(z, lenz, cube[i].p2.z);
        for (int indexX = lx; indexX < rx; indexX++) {
            for (int indexY = ly; indexY < ry; indexY++) {
                for (int indexZ = lz; indexZ < rz; indexZ++) {
                    color[indexX][indexY][indexZ] = 1;
                }
            }
        }
    }
    queue<Point> q;
    q.push({0, 0, 0});
    color[0][0][0] = 2;
    int volume = 1001 * 1001 * 1001;
    int s = 0;
    while(!q.empty()) {
        Point p = q.front();
        q.pop();
        // 内部进行扩展,由于队列中的一定是“空气”所以通过+1的方式来获取某一部分的体积进行扩展
        volume -= (x[p.x + 1] - x[p.x]) * (y[p.y + 1] - y[p.y]) * (z[p.z + 1] - z[p.z]);
        for (int i = 0; i < DIRECTION_NUM; i++) {
            Point now = {p.x + dx[i], p.y + dy[i], p.z + dz[i]};
            if (outBound(now)) continue;
            if (color[now.x][now.y][now.z] == 1) {
                // 在求表面积的时候,根据如何到达当前节点来确定那个面是增加的
                if (dx[i] != 0) { s += (y[now.y + 1] - y[now.y]) * (z[now.z + 1] - z[now.z]);}
                else if (dy[i] != 0) { s += (x[now.x + 1] - x[now.x]) * (z[now.z + 1] - z[now.z]); }
                else { s += (x[now.x + 1] - x[now.x]) * (y[now.y + 1] - y[now.y]); }
            } else if (color[now.x][now.y][now.z] == 0) {
                color[now.x][now.y][now.z] = 2;
                q.push(now);
            }
        }
    }
    cout << s << " " << volume << endl;
}

int main()
{
    ios::sync_with_stdio(false);
    cin >> T;
    while(T--) {
        cin >> n;
        for (int i = 0; i < n; i++) {
            int a, b, c, len1, len2, len3;
            cin >> a >> b >> c >> len1 >> len2 >> len3;
            cube[i] = {a, b, c, a + len1, b + len2, c + len3};
            x[i << 1] = a; y[i << 1] = b; z[i << 1] = c;
            x[i << 1 | 1] = a + len1;
            y[i << 1 | 1] = b + len2;
            z[i << 1 | 1] = c + len3;
        }
        x[n << 1] = y[n << 1] = z[n << 1] = 0;
        x[n << 1 | 1] = y[n << 1 | 1] = z[n << 1 | 1] = 1001;
        discretization();
        floodFill();
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值