题目链接;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 1001∗1001∗1001≈109次,这是肯定要超时的。刘汝佳在紫书中提到了需要先进性离散化在进行染色。这里的离散化实际上就是记录去重后各个坐标轴上分别有多少个点,之后根据原有的坐标在去重后的数组中找到覆盖的范围,进行标记,在进行 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;
}