原题: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\) 开始。
对于左下角的高度为1的方块(即坐标为 \((m - 1, 0, 1)\) 的方块),他的左上顶点位于 \((494, 0)\)。
对于 \((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");
}
}
过于模拟