雕塑 Sculpture UVA12171

题目描述

输入描述

输出描述

 题目大意

某雕塑由 n (n<=50) 个边平行于坐标轴的长方体组成。

每个长方体用 6 个整数 x0, y0, z0, x, y, z 表示(1<= x0, y0, z0, x, y, z <= 500)。 x0 为长方体顶点中,x 坐标的最小值,x 表示长方体在 x 方向的长度。其他 4 个值类似定义。

统计这个雕像的体积和表面积。注意,雕像内部可能会有密闭的空间,其体积应该算在总体积中。

 题目思路

对于这道题来说,离散是需要的,它能够将无穷或者极大转换成有限或者很小,这样能够达到省时间空间的目的。不过我们先不考虑离散,我们就先说floodfill,最后再考虑离散优化,解决题目还是要先考虑主要矛盾的,先从重点开始。

  什么是floodfill呢,就是我们将这个雕塑灌空气或水,不管灌什么,能将这个雕塑覆盖满即可,算体积直接从雕塑入手不是一个好办法,所以不妨试一试总的体积减去外围空气的体积,这样间接的方法能帮助我们进行计算里面的体积了对吧,可以形象的考虑这个问题相当于将一个不规则的物体浸入水中(空气中)。那么我们怎么算外围空气的体积呢,用bfs就行,bfs什么呢,我们从一个个小单位的块来考虑,要dfs就需要将它四周的遍历进去,一共有xyz三个轴6个方向,然后看看是不是空气还是物体就行,那么怎么表示物(空气雕塑)块呢,我们用一个点来表示,那就是这个物块xyz最小值来代表整个。

  下面说一说离散,为什么用离散,原因就是物块所在的空间位置太大了,500*500*500可不行啊,于是我们将大的位置与离散后的位置对应起来,所以在表示物块位置的时候用离散后的坐标来表示,在算体积与表面积时在对应后计算,尽量将离散前的大坐标位置不参与过多的计算,会耗费时空。

/*  Sculpture (UVa12171) */
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;

const int maxn = 105;
const int maxr = 1005;
int dx[] = {1, -1, 0, 0, 0, 0};
int dy[] = {0, 0, 1, -1, 0, 0};
int dz[] = {0, 0, 0, 0, 1, -1};

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

Point p[maxn];
int x[maxn], y[maxn], z[maxn]; //离散化后的坐标轴
int a[maxn][maxn][maxn];       //离散坐标系
int vis[maxn][maxn][maxn];     //标记是否访问
int n, nx, ny, nz;             //数据量以及离散坐标轴的长度
int v, s;

void input();                                 //输入
void build();                                 //填格子
void bfs();                                   //广搜求 v 和 s
int getArea(int tx, int ty, int tz, int dir); //计算 tx ty tz 长方体在 dir 方向上的面积(与空气的接触面积)

int main()
{
    //freopen("input.txt", "r", stdin);
    int T;
    cin >> T;
    while (T--)
    {
        input();
        build();
        bfs();
        cout << s << " " << v << endl;
    }
}

void input()
{
    memset(a, 0, sizeof(a));
    memset(vis, 0, sizeof(vis));
    v = 0;
    s = 0;
    cin >> n;

    for (int i = 1; i <= 2 * n; i += 2)
    {
        cin >> p[i].x >> p[i].y >> p[i].z;
        cin >> p[i + 1].x >> p[i + 1].y >> p[i + 1].z;
        p[i + 1].x += p[i].x;
        p[i + 1].y += p[i].y;
        p[i + 1].z += p[i].z; //算出坐标

        x[i] = p[i].x;
        y[i] = p[i].y;
        z[i] = p[i].z;
        x[i + 1] = p[i + 1].x;
        y[i + 1] = p[i + 1].y;
        z[i + 1] = p[i + 1].z;
    }
    x[0] = 0;
    y[0] = 0;
    z[0] = 0;
    //将坐标轴排序去重
    sort(x, x + 2 * n + 1);
    sort(y, y + 2 * n + 1);
    sort(z, z + 2 * n + 1);
    nx = unique(x, x + 2 * n + 1) - x;
    ny = unique(y, y + 2 * n + 1) - y;
    nz = unique(z, z + 2 * n + 1) - z;
    //在周围围上空气
    x[nx++] = maxr;
    y[ny++] = maxr;
    z[nz++] = maxr;
}

void build()
{
    for (int i = 1; i <= 2 * n; i++)
    {
        int x1, x2, y1, y2, z1, z2; //长方体在离散化后的坐标系中的位置

        x1 = lower_bound(x, x + nx, p[i].x) - x;
        x2 = lower_bound(x, x + nx, p[i + 1].x) - x;
        y1 = lower_bound(y, y + ny, p[i].y) - y;
        y2 = lower_bound(y, y + ny, p[i + 1].y) - y;
        z1 = lower_bound(z, z + nz, p[i].z) - z;
        z2 = lower_bound(z, z + nz, p[i + 1].z) - z;

        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;
    }
}

void bfs()
{
    queue<Point> q;
    q.push(Point(0, 0, 0));
    vis[0][0][0] = 1;

    while (!q.empty())
    {
        Point t = q.front();
        q.pop();
        //如果这一点为空气,则累加空气体积
        if (a[t.x][t.y][t.z] == 0)
            v += (x[t.x + 1] - x[t.x]) * (y[t.y + 1] - y[t.y]) * (z[t.z + 1] - z[t.z]);
        for (int i = 0; i < 6; i++)
        {
            int tx, ty, tz; //与 t 点相邻的点
            tx = t.x + dx[i];
            ty = t.y + dy[i];
            tz = t.z + dz[i];
            if (tx < 0 || tx >= nx - 1 || ty < 0 || ty >= ny - 1 || tz < 0 || tz >= nz - 1) //过滤掉出界的情况
                continue;
            if (a[tx][ty][tz] == 1)
            { //为长方体,则计算接触面积并累加
                s += getArea(tx, ty, tz, i);
            }
            else if (a[tx][ty][tz] == 0 && vis[tx][ty][tz] == 0)
            { //为空气并且没访问过,则加入队列
                q.push(Point(tx, ty, tz));
                vis[tx][ty][tz] = 1;
            }
        }
    }

    v = maxr * maxr * maxr - v;
}

int getArea(int tx, int ty, int tz, int dir)
{
    //不同方向的接触面,面积不同
    if (dx[dir] != 0)
    {
        return (y[ty + 1] - y[ty]) * (z[tz + 1] - z[tz]);
    }
    else if (dy[dir] != 0)
    {
        return (x[tx + 1] - x[tx]) * (z[tz + 1] - z[tz]);
    }
    else
    {
        return (x[tx + 1] - x[tx]) * (y[ty + 1] - y[ty]);
    }
}

 

posted @ 2020-07-27 15:33  我等着你  阅读(149)  评论(0)    收藏  举报