题目描述
你的公司正在建造一个能够抓取小型轻量物体的机器人。机器人需要通过智能判断来确定物体是否足够轻。它通过从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 的网格
- 网格中的每个位置要么是颜色字母,表示从该方向看过去时,该位置看到的第一个不透明立方体的颜色
- 网格中的 '.' 表示从该方向看过去,该位置没有立方体(即整条视线都是透明的)
核心思路
这是一个三维几何约束满足问题 ,我们可以通过迭代约束传播的方法来解决。
算法步骤
- 初始化 :假设所有 N3N^3N3 个立方体都存在
- 处理透明约束:对于每个视图中为 '.' 的位置,移除该视线上的所有立方体
- 迭代处理颜色约束 :
- 对于每个视图中为颜色的位置,沿着视线方向找到第一个存在的立方体
- 如果该立方体颜色未确定,设置为视图颜色
- 如果颜色匹配,处理完成
- 如果颜色冲突,移除该立方体并继续寻找
- 重复步骤3直到没有立方体状态发生变化
- 统计结果:计算剩余立方体数量
坐标映射
关键的难点在于正确建立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个正交投影视图中重构三维物体。关键在于正确建立视图坐标与三维坐标的映射关系,以及通过多次迭代逐步确定或排除立方体的存在性。算法在保证正确性的同时,能够在合理时间内解决问题,体现了约束满足问题在实际应用中的巧妙解法。