GitHub - jzplp/aoapc-UVA-Answer: 算法竞赛入门经典 例题和习题答案 刘汝佳 第二版
这道题目在《算法竞赛入门经典第二版》书中标注了星号,也是第一道出现星号的题目。
第一次遇到星号题,我还是有点害怕的。但是实际上没有那么难,就是有点麻烦而已。
思路:
题目本身比较好理解,实际上就是“三维图”的遍历,一个结点的周围节点是6个而已。
由于中间的空心不计算,因此求体积和表面积时,只需要求外围的体积/面积即可。参考之前做过的UVA-1103 古代象形符号 题目,图片外围加一层边框,保证所有外围空白区域都是联通的。
坐标理论上的最大值是500 * 500 * 500。直接用这么大的图遍历很可能超时,因此参考之前UVA-221 城市正视图 题目,对坐标进行去重和转换,遍历图使用新坐标,计算体积和面积时使用旧坐标。
即使转换了坐标,图最大依旧可以达到100 * 100 * 100,使用dfs还是会爆栈,所以这里使用了bfs即可。(这个题目遍历顺序无所谓的)
下面描述的是我的做法,不一定有普适性:
至少要遍历图两次,第一次求哪些结点是外表面,第二次计算体积和表面积。
对于一个方格,起点的格子是包含在方格内的,但是终点的结点对应的格子是不包含在方格内的,因此在构建图的时候,就要表明哪些结点属于方格的终点,这些点不包含在方格内。还要注意重叠点的情况,如果这个终点属于其他方格内部结点,那么就要被覆盖为内部结点。
如果不标注,就会发生这种情况:

2是方格的终点,2-3中间的区域应该是空的。但是在后续计算面积和体积时,因为2和3都有值,因此认为1-4是一片整个方格。中间的间隔没有被考虑到,因此计算错误。
计算体积时,是用整个500 * 500 * 500的体积减去外围空白区域的体积来计算的,因此这时候需要在计算出的新坐标轴中加入0(最小点)和500(最大点),我实际取的值是505。
计算面积时,由于是精确到每个区域来计算面积,因此会有两种模式:
1. 本身为终点。
这时候就要找该点上部3个面哪些点属于图形内部(本身为终点,不需要考虑下部),确定哪几个面属于终点的面。
2. 本身为外部空白区域贴着的内部结点。这时候就要确定周围哪些结点是属于外部空白区域,以确认哪些是接触面。为了不重复计算,也只是计算上部3个面。因为如果是下部三个面存在接触面,则对应的下部的点就是终点,终点会去计算那些面。
一定要确定是内外接触面,注意这个情况:

里面的空白不要被认错成外部空白区域即可。
AC代码
- #include
- #include
- #include
- #include
- using namespace std;
-
- #define MAXN 505
- #define MAXM 105
-
- const int stepX[6] = {1,-1,0,0,0,0};
- const int stepY[6] = {0,0,1,-1,0,0};
- const int stepZ[6] = {0,0,0,0,1,-1};
-
- struct Box {
- int x0, y0, z0;
- int x, y, z;
- };
-
- struct Point {
- int x, y, z;
- };
-
- Box arrBox[55];
- int boxlen;
-
- int axisX[MAXM];
- int axisY[MAXM];
- int axisZ[MAXM];
- int lenx, leny, lenz;
-
- int space[MAXM][MAXM][MAXM];
- bool findSpace[MAXM][MAXM][MAXM];
- int volume, area;
-
- void clear() {
- memset(arrBox, 0, sizeof(arrBox));
- boxlen = 0;
- memset(axisX, 0, sizeof(axisX));
- memset(axisY, 0, sizeof(axisY));
- memset(axisZ, 0, sizeof(axisZ));
- lenx = 0;
- leny = 0;
- lenz = 0;
- memset(space, 0, sizeof(space));
- volume = 0;
- area= 0;
- memset(findSpace, 0, sizeof(findSpace));
- }
-
- void input() {
- int i;
- Box b;
- scanf("%d", &boxlen);
- for(i = 0; i < boxlen; ++i) {
- scanf("%d %d %d %d %d %d", &b.x0, &b.y0, &b.z0, &b.x, &b.y, &b.z);
- b.x += b.x0;
- b.y += b.y0;
- b.z += b.z0;
- arrBox[i] = b;
- }
- }
-
- void handleAxis() {
- map<int, int> mpx, mpy, mpz;
- int i, j;
- for(i = 0; i < boxlen; ++i) {
- mpx[arrBox[i].x0] = 1;
- mpx[arrBox[i].x] = 1;
- mpy[arrBox[i].y0] = 1;
- mpy[arrBox[i].y] = 1;
- mpz[arrBox[i].z0] = 1;
- mpz[arrBox[i].z] = 1;
- }
- i = 1;
- for(auto ip = mpx.begin(); ip != mpx.end(); ++ip, ++i) {
- mpx[ip->first] = i;
- axisX[i] = ip->first;
- }
- lenx = i;
- axisX[lenx] = MAXN;
- axisX[lenx + 1] = MAXN;
- i = 1;
- for(auto ip = mpy.begin(); ip != mpy.end(); ++ip, ++i) {
- mpy[ip->first] = i;
- axisY[i] = ip->first;
- }
- leny = i;
- axisY[leny] = MAXN;
- axisY[leny + 1] = MAXN;
- i = 1;
- for(auto ip = mpz.begin(); ip != mpz.end(); ++ip, ++i) {
- mpz[ip->first] = i;
- axisZ[i] = ip->first;
- }
- lenz = i;
- axisZ[lenz] = MAXN;
- axisZ[lenz + 1] = MAXN;
- for(i = 0; i < boxlen; ++i) {
- arrBox[i].x0 = mpx[arrBox[i].x0];
- arrBox[i].x = mpx[arrBox[i].x];
- arrBox[i].y0 = mpy[arrBox[i].y0];
- arrBox[i].y = mpy[arrBox[i].y];
- arrBox[i].z0 = mpz[arrBox[i].z0];
- arrBox[i].z = mpz[arrBox[i].z];
- }
- }
-
- bool isEdge(int a) {
- return a / 1000 == 1;
- }
-
- void handleSpace() {
- int i, j, k, a;
- for(a = 0; a < boxlen; ++a) {
- for(i = arrBox[a].x0; i <= arrBox[a].x; ++i) {
- for(j = arrBox[a].y0; j <= arrBox[a].y; ++j) {
- for(k = arrBox[a].z0; k <= arrBox[a].z; ++k) {
- if(i == arrBox[a].x || j == arrBox[a].y || k == arrBox[a].z) {
- if(space[i][j][k] != 1) {
- space[i][j][k] = 1001;
- continue;
- }
- }
- space[i][j][k] = 1;
- }
- }
- }
- }
- }
-
- void getBorder() {
- Point p;
- int i, a, b, c;
- queue
qu; - space[0][0][0] = 2;
- qu.push({0, 0, 0});
- while(!qu.empty()) {
- p = qu.front();
- qu.pop();
- for(i = 0; i < 6; ++i) {
- a = p.x + stepX[i];
- b = p.y + stepY[i];
- c = p.z + stepZ[i];
- if(a < 0 || a > lenx || b < 0 || b > leny || c < 0 || c > lenz) continue;
- if(space[a][b][c] % 1000 == 2) continue;
- if(space[a][b][c] == 0) space[a][b][c] = 2;
- if(isEdge(space[a][b][c])) space[a][b][c] = 1002;
- if(space[a][b][c] % 1000 == 2)
- qu.push({a, b, c});
- }
- }
- }
-
- int getVolumeOne(int a, int b, int c) {
- return (axisX[a+1] - axisX[a]) * (axisY[b+1] - axisY[b]) * (axisZ[c+1] - axisZ[c]);
- }
-
- int getAreaOne(int a, int b, int c) {
- int sum = 0;
- if(isEdge(space[a][b][c])) {
- if(space[a-1][b][c] == 1) {
- sum += (axisY[b+1] - axisY[b]) * (axisZ[c+1] - axisZ[c]);
- }
- if(space[a][b-1][c] == 1) {
- sum += (axisX[a+1] - axisX[a]) * (axisZ[c+1] - axisZ[c]);
- }
- if(space[a][b][c-1] == 1) {
- sum += (axisX[a+1] - axisX[a]) * (axisY[b+1] - axisY[b]);
- }
- }
- if(space[a][b][c] == 1) {
- if(space[a-1][b][c] % 1000 == 2) {
- sum += (axisY[b+1] - axisY[b]) * (axisZ[c+1] - axisZ[c]);
- }
- if(space[a][b-1][c] % 1000 == 2) {
- sum += (axisX[a+1] - axisX[a]) * (axisZ[c+1] - axisZ[c]);
- }
- if(space[a][b][c-1] % 1000 == 2) {
- sum += (axisX[a+1] - axisX[a]) * (axisY[b+1] - axisY[b]);
- }
- }
- return sum;
- }
-
- void getVolumeAndArea() {
- volume = 0;
- area = 0;
- Point p;
- int i, a, b, c;
- queue
qu; - findSpace[0][0][0] = true;
- volume += getVolumeOne(0, 0, 0);
- qu.push({0, 0, 0});
- while(!qu.empty()) {
- p = qu.front();
- qu.pop();
- for(i = 0; i < 6; ++i) {
- a = p.x + stepX[i];
- b = p.y + stepY[i];
- c = p.z + stepZ[i];
- if(a < 0 || a >= lenx || b < 0 || b >= leny || c < 0 || c >= lenz) continue;
- if(findSpace[a][b][c]) continue;
- findSpace[a][b][c] = true;
- if(space[a][b][c] == 1 || space[a][b][c] == 1002) {
- area += getAreaOne(a, b, c);
- }
- if(space[a][b][c] % 1000 == 2) {
- volume += getVolumeOne(a, b, c);
- qu.push({a, b, c});
- }
- }
- }
- }
-
- void outputValue() {
- int i, j ,k;
- for(i = 0; i <= lenx; ++i) {
- printf("%d ", axisX[i]);
- }
- putchar('\n');
- for(i = 0; i <= leny; ++i) {
- printf("%d ", axisY[i]);
- }
- putchar('\n');
- for(i = 0; i <= lenz; ++i) {
- printf("%d ", axisZ[i]);
- }
- putchar('\n');
-
- for(i = 0; i <= lenx; ++i) {
- for(j = 0; j <= leny; ++j) {
- for(k = 0; k <= lenz; ++k) {
- printf("%d %d %d %d\n", i, j, k, space[i][j][k]);
- }
- }
- }
- }
-
- int main() {
- int T;
- scanf("%d", &T);
- while(T--) {
- clear();
- input();
- handleAxis();
- handleSpace();
- getBorder();
- getVolumeAndArea();
- volume = MAXN * MAXN * MAXN - volume;
- printf("%d %d\n", area, volume);
- }
- return 0;
- }