原题:https://fjnuacm.top/d/junior/p/464?tid=62e26edd3a711450d9b817c5

题意

小渊很懒,给你各个位置的高度,让你画立体图。

思路

首先,根据题单名称,我们可以知道要模拟。

方块之间的前后视觉遮挡问题

首先,我们来考虑这个本题最难的点。

显然,对于斜二测画法的立体图,后面靠左的区域会被前者覆盖。那么我们自然会发现,从前往后画,覆盖问题会变得很复杂(不过也不是不能做)。

所以,我们不妨试试从俯视视角的左上角开始画。

我们以一个方块为一个单位开始画图。这里我们定义一个 \(char\) 二维数组来存储一个方块,并用题给的 \(.\) 符号来表示空区域。

char[][] one = {
         "..+---+".toCharArray(),
         "./   /|".toCharArray(),
         "+---+ |".toCharArray(),
         "|   | +".toCharArray(),
         "|   |/.".toCharArray(),
         "+---+..".toCharArray()
 };

然后,对于每一个新加入的方块,我们只要对 \(one\) 进行行列的遍历,如果不为 \(.\),就将前者对应位置覆盖。

定位

每个方块的大小是 \(6 \times 7\),格子的数目也给出了范围,此处我们可以开一个存储数据的容量为 \(500 \times 500\) 的数组(其实是随便输的,可能还不满足最大的容量,只能说测试数据不够全面,反正开大一点就好啦~),那么下角的点坐标就为 \((499, 499)\)

好的,那我们先拿几个图来推一下坐标的式子。

此处定义每个格子的坐标为 \((i, j, h)\)

注:为了更加形象一点,我就直接定义 \(h\) 下标从 \(1\) 开始,而 \(i\)\(j\) 仍从 \(0\) 开始。

image

对于左下角的高度为1的方块(即坐标为 \((m - 1, 0, 1)\) 的方块),他的左上顶点位于 \((494, 0)\)

image

对于 \((m - 2, 1, 1)\) 的方块,他的左上顶点位于 \((492, 2)\)

可见对于横纵坐标的改动,会有以 \(2\) 为倍数的改变,在高度为 \(1\) 时,我们可以推出下面的坐标式子:

\((494 - 2(m - i - 1), 4j + 2(m - i - 1))\)

高度变化对坐标的影响是显而易见的。每当高度加 \(1\),横坐标会减少 \(3\)(不能理解的话可以对照一下题面中两个方块上下相邻的图)。因此我们完善一下式子如下:

\((497 - 2(m - i - 1) - 3h, 4j + 2(m - i - 1))\)

然后我们只需从俯视视角的左上角开始循环添加方块就 \(ok\) 了。

如何输出

在每次添加方格的时候,我们可以记录一下上区间和右区间,上区间就是所有左上顶点的纵坐标最小值,右区间就是所有右上顶点横坐标最大值

对应AC代码 (java)

import java.util.*;

public class Main {

    static char[][] one = {
            "..+---+".toCharArray(),
            "./   /|".toCharArray(),
            "+---+ |".toCharArray(),
            "|   | +".toCharArray(),
            "|   |/.".toCharArray(),
            "+---+..".toCharArray()
    };

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        char[][] ans = new char[500][500]; //开数组
        for(int i=0;i<500;i++) for(int j=0;j<500;j++) ans[i][j] = '.'; //空位置填上占位符"."
        int m = scanner.nextInt(), n = scanner.nextInt();
        int[][] height = new int[m][n];
        for(int i=0;i<m;i++) for(int j=0;j<n;j++) height[i][j] = scanner.nextInt();
        int top = 500, right = 0;
        for(int i=0;i<m;i++) for(int j=0;j<n;j++) for(int h=1;h<=height[i][j];h++){
            //从左上角画起,存到数组底部,到时候记录最高和最右的位置即可
            int startI = 497 - 2 * (m - i - 1) - 3 * h, startJ = 4 * j + 2 * (m - i - 1);
            top = Math.min(top, startI);
            right = Math.max(right, startJ + 6);
            for(int p=0;p<6;p++) for(int q=0;q<7;q++){
                if(one[p][q] != '.') ans[startI + p][startJ + q] = one[p][q];
            }
        }
        for(int i=top;i<=499;i++) {
            for(int j=0;j<=right;j++) System.out.print(ans[i][j]);
            System.out.println();
        }
    }
}

对应AC代码 (cpp)

#include <bits/stdc++.h>

using namespace std;

char one[6][8] = {
        "..+---+",
        "./   /|",
        "+---+ |",
        "|   | +",
        "|   |/.",
        "+---+.."
};

char ans[500][500]; //开数组
int height[52][52];

int main() {
    for (int i = 0; i < 500; i++) for (int j = 0; j < 500; j++) ans[i][j] = '.'; //空位置填上占位符"."
    int m, n;
    scanf("%d %d", &m, &n);
    for (int i = 0; i < m; i++) for (int j = 0; j < n; j++) scanf("%d", &height[i][j]);
    int top = 500, right = 0;
    for (int i = 0; i < m; i++)
        for (int j = 0; j < n; j++)
            for (int h = 1; h <= height[i][j]; h++) {
                //从左上角画起,存到数组底部,到时候记录最高和最右的位置即可
                int startI = 497 - 2 * (m - i - 1) - 3 * h, startJ = 4 * j + 2 * (m - i - 1);
                top = min(top, startI);
                right = max(right, startJ + 6);
                for (int p = 0; p < 6; p++) 
                    for (int q = 0; q < 7; q++) {
                        if (one[p][q] != '.') ans[startI + p][startJ + q] = one[p][q];
                    }
            }
    for (int i = top; i <= 499; i++) {
        for (int j = 0; j <= right; j++) printf("%c", ans[i][j]);
        printf("\n");
    }
}

过于模拟