例题6-18 雕塑 UVa12171

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



你可能感兴趣的:(uva)