1.题目描述:点击打开链接
2.解题思路:这道题是紫书上的例题,不过只有解题思路,意思是先将外围包裹一圈空气,然后从空气入手进行floodfill,这之前还要进行“离散化”处理,最后计算体积时用总体积减去外围空气的体积;经过了三天的思考与编程,终于编出来了,不过总是RE,没办法只好换了一种思路:也是从外围空气入手,但直接统计体积和表面积。最后终于AC了,不容易啊。。。
3.代码:
#define _CRT_SECURE_NO_WARNINGS
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
#include<string>
#include<map>
#include<sstream>
#include<queue>
#include<cctype>
using namespace std;
typedef long long ll;
const int maxn = 100 + 30;
const int maxm = 50 + 5;
int g[maxn][maxn][maxn];
int x[maxn], y[maxn], z[maxn];
const int dx[] = { 1, -1, 0, 0, 0, 0 };
const int dy[] = { 0, 0, 1, -1, 0, 0 };
const int dz[] = { 0, 0, 0, 0, 1, -1 };
map<int, int>X, Y, Z;
int n;
ll space, vol, air;
int xnum, ynum, znum;
int mx, my, mz;
struct rec
{
int id;
int x0, y0, z0, x, y, z;
bool operator <(const rec&b)
{
return x0 < b.x0 || (x0 == b.x0&&y0 < b.y0) || (x0 == b.x0&&y0 == b.y0&&z0 < b.z0);
}
}r[maxm];
struct coord
{
int x, y, z;
coord(int i, int j, int k) :x(i), y(j), z(k){}
};
void draw(int pos, int id)
{
int x1 = X[r[pos].x0], x2 = X[r[pos].x + r[pos].x0];
int y1 = Y[r[pos].y0], y2 = Y[r[pos].y + r[pos].y0];
int z1 = Z[r[pos].z0], z2 = Z[r[pos].z + r[pos].z0];
for (int i = x1; i < x2; i++)
for (int j = y1; j < y2; j++)
for (int k = z1; k < z2; k++)
g[i][j][k] = id;
}
int main()
{
int t;
cin >> t;
while (t--)
{
air = 0;
cin >> n;
memset(g, 0, sizeof(g));
memset(x, 0, sizeof(x));
memset(y, 0, sizeof(y));
memset(z, 0, sizeof(z));
for (int i = 0; i < n; i++)
{
scanf("%d%d%d%d%d%d", &r[i].x0, &r[i].y0, &r[i].z0, &r[i].x, &r[i].y, &r[i].z);
r[i].id = i + 1;
x[i * 2 + 1] = r[i].x0, x[i * 2 + 2] = r[i].x + r[i].x0;
y[i * 2 + 1] = r[i].y0, y[i * 2 + 2] = r[i].y + r[i].y0;
z[i * 2 + 1] = r[i].z0, z[i * 2 + 2] = r[i].z + r[i].z0;
}
sort(r, r + n);
sort(x + 1, x + n * 2 + 1);
sort(y + 1, y + n * 2 + 1);
sort(z + 1, z + n * 2 + 1);
xnum = unique(x + 1, x + n * 2 + 1) - (x + 1);
ynum = unique(y + 1, y + n * 2 + 1) - (y + 1);
znum = unique(z + 1, z + n * 2 + 1) - (z + 1);
x[0] = y[0] = z[0] = -1;
vol = 0;
space = 0;
for (int i = 1; i <= xnum; i++)
X[x[i]] = i;
for (int i = 1; i <= ynum; i++)
Y[y[i]] = i;
for (int i = 1; i <= znum; i++)
Z[z[i]] = i;
for (int i = 0; i < n; i++)
draw(i, r[i].id);
vector<coord>q;
q.push_back(coord(0, 0, 0));
while (!q.empty())
{
coord cd = q.back(); q.pop_back();
int i = cd.x, j = cd.y, k = cd.z;
for (int d = 0; d < 6; d++)
{
int nx = i + dx[d];
int ny = j + dy[d];
int nz = k + dz[d];
if (nx >= 0 && ny >= 0 && nz >= 0 && nx <= xnum && ny <= ynum & nz <= znum && !g[nx][ny][nz])
{
g[nx][ny][nz] = -1;
q.push_back(coord(nx, ny, nz));
}
}
}
for (int i = 1; i < xnum;i++)
for (int j = 1; j < ynum;j++)
for (int k = 1; k < znum; k++)
{
int l1 = x[i + 1] - x[i], l2 = y[j + 1] - y[j], l3 = z[k + 1] - z[k];
if (g[i][j][k] != -1)vol += l1*l2*l3;
for (int d = 0; d < 6;d++)
if (g[i][j][k] != -1 && g[i + dx[d]][j + dy[d]][k + dz[d]] == -1)
space += dx[d] ? l2*l3 : dy[d] ? l1*l3 : l1*l2;
}
cout << space << ' ' << vol << endl;
}
return 0;
}
最后附上一份参考代码:
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<algorithm>
#include<string>
#include<sstream>
#include<set>
#include<vector>
#include<stack>
#include<map>
#include<queue>
#include<deque>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<ctime>
#include<functional>
using namespace std;
typedef long long ll;
typedef vector<int> vi;
const int dx[] = { -1, 0, 0, 1, 0, 0 };
const int dy[] = { 0, -1, 0, 0, 1, 0 };
const int dz[] = { 0, 0, -1, 0, 0, 1 };
int compress(int n, int *x, int *v) {//进行状态压缩,之后每个边的实际长度存放在v数组,
memcpy(v, x, sizeof(int)*n);
v[n] = -1;//设置该点的作用是让最小的下标从1开始,,从1开始还有一个作用是将来能够给外围加一圈空气;
sort(v, v + n + 1);
int m = unique(v, v + n + 1) - v;
for (int i = 0; i < n; ++i)
for (int j = 0; j < m; ++j) if (x[i] == v[j]) { x[i] = j; break; }
return m;//返回的是左闭右开区间的长度
}
int occ[110][110][110];
void solve() {
int N;
scanf("%d", &N);
int X[150], Y[150], Z[150];
int xv[150], yv[150], zv[150];
for (int i = 0; i < N; ++i) {
scanf("%d%d%d%d%d%d", X + 2 * i, Y + 2 * i, Z + 2 * i, X + 2 * i + 1, Y + 2 * i + 1, Z + 2 * i + 1);
X[2 * i + 1] += X[2 * i]; Y[2 * i + 1] += Y[2 * i]; Z[2 * i + 1] += Z[2 * i];
}
int xn = compress(2 * N, X, xv);
int yn = compress(2 * N, Y, yv);
int zn = compress(2 * N, Z, zv);
memset(occ, 0, sizeof(occ));
for (int i = 0; i < N; ++i)
for (int x = X[2 * i]; x < X[2 * i + 1]; ++x)
for (int y = Y[2 * i]; y < Y[2 * i + 1]; ++y)
for (int z = Z[2 * i]; z < Z[2 * i + 1]; ++z)
occ[x][y][z] = true;//标记所有有长方体的区域,注意是左闭右开区间
vi Q;
Q.push_back(0);
occ[0][0][0] = -1;
while (!Q.empty()) {//从外围进行BFS,加一圈“空气”,用-1表示
int v = Q.back(); Q.pop_back();
int x = v & 0xFF, y = (v >> 8) & 0xFF, z = (v >> 16) & 0xFF;//此处的编码,解码技巧值得学习,但要注意此时每个数不能超过255
for (int d = 0; d < 6; ++d) {
int nx = x + dx[d], ny = y + dy[d], nz = z + dz[d];
if (nx >= 0 && ny >= 0 && nz >= 0 && nx < xn && ny < yn && nz < zn &&
!occ[nx][ny][nz]) {
occ[nx][ny][nz] = -1;
Q.push_back((nz << 16) | (ny << 8) | nx);
}
}
}
ll vol = 0, area = 0;
for (int x = 1; x < xn - 1; ++x)
for (int y = 1; y < yn - 1; ++y)
for (int z = 1; z < zn - 1; ++z) {
int sx = xv[x + 1] - xv[x], sy = yv[y + 1] - yv[y], sz = zv[z + 1] - zv[z];
if (occ[x][y][z] != -1) vol += (ll)sx*sy*sz;//该处存在方块,注意一定要写成“不等于-1”,因为此时内部的空气是0
for (int d = 0; d < 6; ++d)
if (occ[x][y][z] != -1 && occ[x + dx[d]][y + dy[d]][z + dz[d]] == -1)//点(x,y,z)处有方块,但延长一个单位方向后却是外围空气
area += dx[d] ? sy*sz : dy[d] ? sx*sz : sx*sy;
}
printf("%lld %lld\n", area, vol);
}
int main(void) {
int N;
for (scanf("%d", &N); N--; solve());
return 0;
}