按后→前、左→右、下→上的画家算法,把固定 7×6 字形覆盖到字符画布上生成立体图。
OJ: roj
题目 ID: 3563
难度:普及
标签:模拟python
创建: 2026-10-02 07:50
更新: 2026-10-06 14:09
题目描述
给定 +---+、/ /| 等字符拼出正视投影,输出字符矩阵。
思路
按"从后往前、从左往右"的次序画每根柱子:先画顶面 +---+ 与侧面 /、|,高处的柱子自然遮挡低处,逐格把对应字符写入画布,最后逐行输出。
参考代码
Python 版:
python
#!/usr/bin/env python3
# Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
# rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
# rainboy的学习导航网站: https://idx.roj.ac.cn
# create_at: 2026-10-02 08:04
# update_at: 2026-10-02 08:04
import sys
# 一个积木的字形:以前面左下角顶点为锚点 (0,0),dy 向上为负、dx 向右为正。
# 三张可见面(前面、上面、右面)连边框一起整格落笔,面的内部写空格,
# 这样后画的积木能先一步抹掉被遮挡的笔画,遮挡无需单独判断。
GLYPH = (
(-5, 2, "+---+"), # 上面的后沿
(-4, 1, "/ /|"), # 上面的左右斜边与内部
(-3, 0, "+---+ |"), # 前面的上沿 + 右面的上半
(-2, 0, "| | +"), # 前面中段 + 右面内部与后沿端点
(-1, 0, "| |/"), # 前面中段 + 右面的下斜边
(0, 0, "+---+"), # 前面的下沿
)
CUBE = [(dy, dx + k, ch) for dy, dx, seg in GLYPH for k, ch in enumerate(seg)] # 36 格
def paint(canvas: list[list[str]], height: list[list[int]], rows: int) -> None:
"""画家算法:按后排→前排、左→右、下→上,把每个积木的 36 格字形覆盖上画布。
这个顺序保证右邻盖掉本格右面、前排盖掉后排、上层盖掉下层顶面;
锚点坐标由斜投影的线性位移给出:行偏移 depth、列每格 4 列、层每层 3 行。
"""
m, n = len(height), len(height[0])
for i in range(m):
depth = 2 * (m - 1 - i) # 第 i 行相对前排往“后上”偏移:行数与列数共用
base_y = rows - 1 - depth # 该行最下层积木锚点所在行(前排在最后一行)
for j in range(n):
base_x = 4 * j + depth # 同一锚点列
for k in range(height[i][j]): # k = 0 起自下而上
y = base_y - 3 * k # 第 k 层锚点行,每上一层抬 3 行
for dy, dx, ch in CUBE:
canvas[y + dy][base_x + dx] = ch
def solve() -> None:
data = iter(map(int, sys.stdin.buffer.read().split()))
m, n = next(data), next(data)
height: list[list[int]] = [] # height[i][j] = 第 i 行第 j 列的积木数
for _ in range(m):
height.append([next(data) for _ in range(n)])
# 画布取包络:宽 = 最右锚点 + 字形 6 列 + 1;高使最高塔字形顶行恰为 0。
width = 4 * (n - 1) + 2 * (m - 1) + 7
rows = max(3 * height[i][j] + 2 * (m - 1 - i) for i in range(m) for j in range(n)) + 3
canvas = [["."] * width for _ in range(rows)]
paint(canvas, height, rows)
sys.stdout.write("\n".join(map("".join, canvas)) + "\n")
if __name__ == "__main__":
solve()C++ 版(同一算法):
cpp
/**
* Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
* rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
* rainboy的学习导航网站: https://idx.roj.ac.cn
* create_at: 2026-10-06 14:05
* update_at: 2026-10-06 14:05
*/
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
typedef long long ll;
const int MAXM = 55;
const int MAXN = 55;
// 单个积木的 7x6 字形,以锚点 (dy=0,dx=0) 为前面左下角
// 每行: dy 偏移, dx 起始偏移, 字符串
struct GlyphRow {
int dy;
int dx;
const char *s;
};
const GlyphRow GLYPH[6] = {
{-5, 2, "+---+"}, // 上面后沿
{-4, 1, "/ /|"}, // 上面
{-3, 0, "+---+ |"}, // 前面上沿 + 右面上半
{-2, 0, "| | +"}, // 前面 + 右面
{-1, 0, "| |/"}, // 前面 + 右面下斜
{0, 0, "+---+"}, // 前面下沿
};
// 展开成 36 个落笔点,方便直接覆盖
struct Cell {
int dy;
int dx;
char ch;
};
Cell cube[36];
int cube_cnt = 0;
int m, n;
int h[MAXM][MAXN];
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
// 展开字形到 cube[]
for (int r = 0; r < 6; ++r) {
int dy = GLYPH[r].dy;
int dx0 = GLYPH[r].dx;
const char *s = GLYPH[r].s;
for (int k = 0; s[k]; ++k) {
cube[cube_cnt].dy = dy;
cube[cube_cnt].dx = dx0 + k;
cube[cube_cnt].ch = s[k];
++cube_cnt;
}
}
if (!(cin >> m >> n)) return 0;
for (int i = 0; i < m; ++i)
for (int j = 0; j < n; ++j)
cin >> h[i][j];
// 画布包络尺寸
int width = 4 * (n - 1) + 2 * (m - 1) + 7;
int rows = 0;
for (int i = 0; i < m; ++i)
for (int j = 0; j < n; ++j)
rows = max(rows, 3 * h[i][j] + 2 * (m - 1 - i));
rows += 3; // 顶行到锚点还需 3 行余量
// 画布,背景用 '.'
vector<string> canvas(rows, string(width, '.'));
// 画家算法:后排->前排,左->右,下层->上层
for (int i = 0; i < m; ++i) {
int depth = 2 * (m - 1 - i); // 行 i 相对前排的后移量
int base_y = rows - 1 - depth; // 该行最下层锚点行
for (int j = 0; j < n; ++j) {
int base_x = 4 * j + depth; // 锚点列
for (int k = 0; k < h[i][j]; ++k) {
int y = base_y - 3 * k; // 第 k 层锚点行
for (int c = 0; c < cube_cnt; ++c) {
int rr = y + cube[c].dy;
int cc = base_x + cube[c].dx;
canvas[rr][cc] = cube[c].ch;
}
}
}
}
for (int i = 0; i < rows; ++i)
cout << canvas[i] << "\n";
return 0;
}


