UVa 1030 Image Is Everything

题目描述

你的公司正在建造一个能够抓取小型轻量物体的机器人。机器人需要通过智能判断来确定物体是否足够轻。它通过从666个基本方向(前、后、左、右、上、下)拍摄物体的图片,然后根据这些图像推断物体重量的上限。

每个物体由一个 N×N×NN \times N \times NN×N×N 的立方体网格组成,其中某些立方体可能缺失。每个 1×1×11 \times 1 \times 11×1×1 的立方体重111克,每个立方体被涂成单一纯色。物体不一定是连通的。

输入格式

输入包含多个测试用例。每个测试用例以一行包含整数 NNN (1≤N≤101 \leq N \leq 101≤N≤10) 开始,表示物体的大小。接下来的 NNN 行包含666个不同的 N×NN \times NN×N 视图,按顺序为:前、左、后、右、顶、底视图。每个视图与下一个视图之间用单个空格分隔。

输出格式

对于每个测试用例,输出一行包含物体的最大可能重量,格式如样例所示。

题目分析

问题理解

我们需要根据666个正交投影视图来重构一个 N×N×NN \times N \times NN×N×N 的三维物体,并找出在满足所有视图约束的情况下,物体可能包含的最大立方体数量。

关键约束条件:

  • 每个视图是一个 N×NN \times NN×N 的网格
  • 网格中的每个位置要么是颜色字母,表示从该方向看过去时,该位置看到的第一个不透明立方体的颜色
  • 网格中的 '.' 表示从该方向看过去,该位置没有立方体(即整条视线都是透明的)

核心思路

这是一个三维几何约束满足问题 ,我们可以通过迭代约束传播的方法来解决。

算法步骤
  1. 初始化 :假设所有 N3N^3N3 个立方体都存在
  2. 处理透明约束:对于每个视图中为 '.' 的位置,移除该视线上的所有立方体
  3. 迭代处理颜色约束
    • 对于每个视图中为颜色的位置,沿着视线方向找到第一个存在的立方体
    • 如果该立方体颜色未确定,设置为视图颜色
    • 如果颜色匹配,处理完成
    • 如果颜色冲突,移除该立方体并继续寻找
  4. 重复步骤3直到没有立方体状态发生变化
  5. 统计结果:计算剩余立方体数量
坐标映射

关键的难点在于正确建立666个视图与三维立方体坐标之间的映射关系:

  • 前视图 (−z-z−z 方向):(x,y,z)=(c,N−1−r,d)(x, y, z) = (c, N-1-r, d)(x,y,z)=(c,N−1−r,d)
  • 左视图 (−x-x−x 方向):(x,y,z)=(d,N−1−r,N−1−c)(x, y, z) = (d, N-1-r, N-1-c)(x,y,z)=(d,N−1−r,N−1−c)
  • 后视图 (+z+z+z 方向):(x,y,z)=(N−1−c,N−1−r,N−1−d)(x, y, z) = (N-1-c, N-1-r, N-1-d)(x,y,z)=(N−1−c,N−1−r,N−1−d)
  • 右视图 (+x+x+x 方向):(x,y,z)=(N−1−d,N−1−r,c)(x, y, z) = (N-1-d, N-1-r, c)(x,y,z)=(N−1−d,N−1−r,c)
  • 顶视图 (+y+y+y 方向):(x,y,z)=(c,N−1−d,N−1−r)(x, y, z) = (c, N-1-d, N-1-r)(x,y,z)=(c,N−1−d,N−1−r)
  • 底视图 (−y-y−y 方向):(x,y,z)=(c,d,r)(x, y, z) = (c, d, r)(x,y,z)=(c,d,r)

其中 (r,c)(r, c)(r,c) 是视图坐标,ddd 是视线深度。

复杂度分析

  • 空间复杂度 :O(N3)O(N^3)O(N3),用于存储三维立方体状态
  • 时间复杂度 :最坏情况下 O(N5)O(N^5)O(N5),但由于 N≤10N \leq 10N≤10,实际运行效率很高

代码实现

cpp 复制代码
// Image Is Everything
// UVa ID: 1030
// Verdict: Accepted
// Submission Date: 2025-10-24
// UVa Run Time: 0.000s
//
// 版权所有(C)2025,邱秋。metaphysis # yeah dot net

#include <bits/stdc++.h>

using namespace std;

const int MAX_CUBE_SIZE = 15;  // 最大立方体尺寸

class CubeReconstructor {
private:
    int cubeSize;                          // 立方体的尺寸 N
    char viewData[6][MAX_CUBE_SIZE][MAX_CUBE_SIZE];  // 存储6个方向的视图数据
    char cubeState[MAX_CUBE_SIZE][MAX_CUBE_SIZE][MAX_CUBE_SIZE];  // 三维立方体状态
    
    /**
     * 读取有效字符,跳过空格等无效字符
     * @return 有效的颜色字符(大写字母)或 '.' 表示透明
     */
    char readValidCharacter() {
        char currentChar;
        while (true) {
            currentChar = getchar();
            // 只接受大写字母(颜色)或 '.'(透明)
            if ((currentChar >= 'A' && currentChar <= 'Z') || currentChar == '.') {
                return currentChar;
            }
        }
    }
    
    /**
     * 将视图坐标转换为三维立方体坐标
     * @param viewId 视图编号(0-5分别对应前、左、后、右、顶、底视图)
     * @param viewRow 视图中的行坐标
     * @param viewCol 视图中的列坐标
     * @param depth 视线深度(从近到远)
     * @param cubeX 输出的立方体X坐标
     * @param cubeY 输出的立方体Y坐标
     * @param cubeZ 输出的立方体Z坐标
     */
    void convertViewToCubeCoordinates(int viewId, int viewRow, int viewCol, int depth, 
                                     int& cubeX, int& cubeY, int& cubeZ) {
        switch(viewId) {
            case 0:  // 前视图 (front): 从 -Z 方向观察
                cubeX = viewRow;
                cubeY = viewCol;
                cubeZ = depth;
                break;
            case 1:  // 左视图 (left): 从 -X 方向观察
                cubeX = viewRow;
                cubeY = depth;
                cubeZ = cubeSize - viewCol - 1;
                break;
            case 2:  // 后视图 (back): 从 +Z 方向观察
                cubeX = viewRow;
                cubeY = cubeSize - viewCol - 1;
                cubeZ = cubeSize - depth - 1;
                break;
            case 3:  // 右视图 (right): 从 +X 方向观察
                cubeX = viewRow;
                cubeY = cubeSize - depth - 1;
                cubeZ = viewCol;
                break;
            case 4:  // 顶视图 (top): 从 +Y 方向观察
                cubeX = depth;
                cubeY = viewCol;
                cubeZ = cubeSize - viewRow - 1;
                break;
            case 5:  // 底视图 (bottom): 从 -Y 方向观察
                cubeX = cubeSize - depth - 1;
                cubeY = viewCol;
                cubeZ = viewRow;
                break;
        }
    }

public:
    /**
     * 初始化重构器,读取输入数据
     * @param size 立方体的尺寸 N
     */
    void initialize(int size) {
        cubeSize = size;
        
        // 读取6个方向的视图数据
        // 输入顺序:对于每一行,依次读取前、左、后、右、顶、底视图的对应行
        for (int row = 0; row < cubeSize; row++) {
            for (int viewId = 0; viewId < 6; viewId++) {
                for (int col = 0; col < cubeSize; col++) {
                    viewData[viewId][row][col] = readValidCharacter();
                }
            }
        }
        
        // 初始化立方体状态:'#' 表示立方体可能存在(初始状态)
        for (int x = 0; x < cubeSize; x++) {
            for (int y = 0; y < cubeSize; y++) {
                for (int z = 0; z < cubeSize; z++) {
                    cubeState[x][y][z] = '#';
                }
            }
        }
    }
    
    /**
     * 计算物体的最大可能重量
     * @return 最大重量(即存在的立方体数量)
     */
    int calculateMaximumWeight() {
        int cubeX, cubeY, cubeZ;  // 立方体坐标

        // 第一步:处理透明视图约束
        // 对于每个视图中标记为 '.' 的位置,移除整条视线上的所有立方体
        for (int viewId = 0; viewId < 6; viewId++) {
            for (int viewRow = 0; viewRow < cubeSize; viewRow++) {
                for (int viewCol = 0; viewCol < cubeSize; viewCol++) {
                    if (viewData[viewId][viewRow][viewCol] == '.') {
                        // 沿着视线方向,将所有立方体标记为不存在
                        for (int depth = 0; depth < cubeSize; depth++) {
                            convertViewToCubeCoordinates(viewId, viewRow, viewCol, depth, 
                                                        cubeX, cubeY, cubeZ);
                            cubeState[cubeX][cubeY][cubeZ] = '.';  // 标记为不存在
                        }
                    }
                }
            }
        }

        // 第二步:迭代处理颜色约束
        bool hasChanges;  // 标记本轮迭代是否有状态变化
        do {
            hasChanges = false;  // 初始假设没有变化
            
            // 遍历所有视图的所有颜色位置
            for (int viewId = 0; viewId < 6; viewId++) {
                for (int viewRow = 0; viewRow < cubeSize; viewRow++) {
                    for (int viewCol = 0; viewCol < cubeSize; viewCol++) {
                        char expectedColor = viewData[viewId][viewRow][viewCol];
                        // 只处理颜色位置,跳过透明位置
                        if (expectedColor != '.') {
                            // 沿着视线方向寻找第一个存在的立方体
                            for (int depth = 0; depth < cubeSize; depth++) {
                                convertViewToCubeCoordinates(viewId, viewRow, viewCol, depth,
                                                            cubeX, cubeY, cubeZ);
                                
                                // 如果当前立方体已确定不存在,继续检查下一个
                                if (cubeState[cubeX][cubeY][cubeZ] == '.') {
                                    continue;
                                }
                                
                                // 如果立方体颜色未确定,设置为期望颜色
                                if (cubeState[cubeX][cubeY][cubeZ] == '#') {
                                    cubeState[cubeX][cubeY][cubeZ] = expectedColor;
                                }
                                
                                // 如果颜色匹配,这个视图位置处理完成
                                if (cubeState[cubeX][cubeY][cubeZ] == expectedColor) {
                                    break;
                                }
                                
                                // 颜色冲突:立方体颜色与视图要求不符,必须移除
                                cubeState[cubeX][cubeY][cubeZ] = '.';
                                hasChanges = true;  // 标记有状态变化,需要继续迭代
                            }
                        }
                    }
                }
            }
        } while (hasChanges);  // 继续迭代直到没有状态变化

        // 统计最终存在的立方体数量
        int existingCubeCount = 0;
        for (int x = 0; x < cubeSize; x++) {
            for (int y = 0; y < cubeSize; y++) {
                for (int z = 0; z < cubeSize; z++) {
                    if (cubeState[x][y][z] != '.') {
                        existingCubeCount++;
                    }
                }
            }
        }
        
        return existingCubeCount;  // 每个立方体重1克,所以数量即为重量
    }
};

int main() {
    int cubeSize;
    CubeReconstructor reconstructor;
    
    // 处理多个测试用例,直到输入0为止
    while (scanf("%d", &cubeSize) == 1 && cubeSize != 0) {
        reconstructor.initialize(cubeSize);
        int maxWeight = reconstructor.calculateMaximumWeight();
        printf("Maximum weight: %d gram(s)\n", maxWeight);
    }
    
    return 0;
}

总结

本题通过迭代约束传播 的方法,从666个正交投影视图中重构三维物体。关键在于正确建立视图坐标与三维坐标的映射关系,以及通过多次迭代逐步确定或排除立方体的存在性。算法在保证正确性的同时,能够在合理时间内解决问题,体现了约束满足问题在实际应用中的巧妙解法。

相关推荐
一个不知名程序员www18 小时前
算法学习入门---vector(C++)
c++·算法
云飞云共享云桌面18 小时前
无需配置传统电脑——智能装备工厂10个SolidWorks共享一台工作站
运维·服务器·前端·网络·算法·电脑
福尔摩斯张18 小时前
《C 语言指针从入门到精通:全面笔记 + 实战习题深度解析》(超详细)
linux·运维·服务器·c语言·开发语言·c++·算法
橘颂TA18 小时前
【剑斩OFFER】算法的暴力美学——两整数之和
算法·leetcode·职场和发展
xxxxxxllllllshi19 小时前
【LeetCode Hot100----14-贪心算法(01-05),包含多种方法,详细思路与代码,让你一篇文章看懂所有!】
java·数据结构·算法·leetcode·贪心算法
前端小L19 小时前
图论专题(二十二):并查集的“逻辑审判”——判断「等式方程的可满足性」
算法·矩阵·深度优先·图论·宽度优先
铁手飞鹰19 小时前
二叉树(C语言,手撕)
c语言·数据结构·算法·二叉树·深度优先·广度优先
专业抄代码选手20 小时前
【Leetcode】1930. 长度为 3 的不同回文子序列
javascript·算法·面试
[J] 一坚21 小时前
深入浅出理解冒泡、插入排序和归并、快速排序递归调用过程
c语言·数据结构·算法·排序算法
czlczl2002092521 小时前
算法:二叉搜索树的最近公共祖先
算法