信息学奥赛一本通 1977:【08NOIP普及组】立体图 | 洛谷 P1058 [NOIP2008 普及组] 立体图

【题目链接】

ybt 1977:【08NOIP普及组】立体图
洛谷 P1058 [NOIP2008 普及组] 立体图

【题目考点】

1. 模拟

【解题思路】

记长的方向为x轴,宽的方向为y轴,高的方向为z轴。
输入矩阵中的第i行j列表示的格子,为三维坐标系中x轴方向第j位置,y轴方向第m-i+1位置。

1. 确定画布大小

先确定画布大小,也就是最后输出的K行L列字符串矩阵的K与L分别是多少。

我们先看单独一个积木的图像所占的字符:横向:7,纵向:6 
..+---+
./   /|
+---+ |
|   | +
|   |/.
+---+..
x轴增加一个方块后:
..+---+---+ 
./   /   /|
+---+---+ |
|   |   | +
|   |   |/.
+---+---+..
整个图像所占字符为:横向:11,纵向:6,横向增加了4个字符。
y轴增加一个方块后:
....+---+ 
.../   /|
..+---+ |
./   /| +
+---+ |/.
|   | +..
|   |/...
+---+....
整个图像所占字符为:横向:9,纵向:8,横纵分别增加了2个字符。
z轴增加一个方块后:
..+---+
./   /|
+---+ |
|   | +
|   |/|
+---+ |
|   | +
|   |/.
+---+..
整个图像所占字符为:横向:7,纵向:9,纵向增加了3个字符。

每个坐标轴上的积木从1开始数。
想要把第(a, b, c)位置的积木画在图像中,图像横向(列)要达到 7 + ( a − 1 ) ∗ 4 + ( b − 1 ) ∗ 2 7+(a-1)*4+(b-1)*2 7+(a1)4+(b1)2, 纵向(行)要达到 6 + ( c − 1 ) ∗ 3 + ( b − 1 ) ∗ 2 6+(c-1)*3+(b-1)*2 6+(c1)3+(b1)2
题目指明,每个格子至少有1个积木。当a为n,b为m时,图像横向长度最大, L = 7 + ( n − 1 ) ∗ 4 + ( m − 1 ) ∗ 2 L=7+(n-1)*4+(m-1)*2 L=7+(n1)4+(m1)2。对于每个格子上的积木,其中最高的那个积木会让图像纵向长度最大,比较每个格子上最高的积木,K取 6 + ( c − 1 ) ∗ 3 + ( b − 1 ) ∗ 2 6+(c-1)*3+(b-1)*2 6+(c1)3+(b1)2的最大值。

考虑极端最大值,m、n最大为50,高度最大100,图像横向最大为 7 + ( 50 − 1 ) ∗ 4 + ( 50 − 1 ) ∗ 2 = 301 7+(50-1)*4+(50-1)*2=301 7+(501)4+(501)2=301,纵向最大为 6 + ( 100 − 1 ) ∗ 3 + ( 50 − 1 ) ∗ 2 = 401 6+(100-1)*3+(50-1)*2=401 6+(1001)3+(501)2=401
画布最大长宽可以设为405即可。
char mp[405][405]表示画布,mp[i][j]表示以左下角为(1,1)的第i行第j列的位置。先把画布中横向L纵向K的区域都设为’.'。

2. 画积木

设一个函数void draw(int x, int y, int z)表示画一个积木,积木在三维坐标系中的坐标为(x, y, z)

  • 积木x轴位置每增大1,积木左下角在画布中的横向位置增大4。
  • 积木y轴位置每增大1,积木左下角在画布中的横向位置增加2,纵向位置增加2。
  • 积木z轴位置每增大1,积木左下角在画布中的纵向位置增加3。

分析规律可知,如积木坐标为(x, y, z),它的左下角’+'在画布中的位置为:横向位置(列): 1 + ( x − 1 ) ∗ 4 + ( y − 1 ) ∗ 2 1+(x-1)*4+(y-1)*2 1+(x1)4+(y1)2, 纵向位置(行): 1 + ( z − 1 ) ∗ 3 + ( y − 1 ) ∗ 2 1+(z-1)*3+(y-1)*2 1+(z1)3+(y1)2

定位了左下角’+‘的位置后,即可在画布中画出这个积木。
这里可以将积木图像上各个相对位置上的字符保存在char co[10][10]co[i][j]指的是以左下角为坐标原点,单独一个积木的图像中第i行第j列的字符。(因此在初始化co数组时,要上下颠倒)。
只要知道左下角在画布的位置,就可以根据co数组获取其相对位置上的积木图像的字符,覆盖原字符。如果这里是’.',则不覆盖原字符。

要画多个积木,根据透视原理,有如下规律:

  • 前面的积木会挡住后边的,所以应该先画后边的,再画前面的。
  • 右边的积木会挡住左边的,所以应该先画左边的,再画右边的。
  • 上面的积木会挡住下面的,所以应该先画下边的,再画上边的。

只要满足上述顺序画积木,便不会有问题。这里选取可能的一种顺序:
先画最后一面墙的积木(也就是y轴上位置最大的积木),对于这面墙的积木,从左向右分别画出每列的积木,对于每列积木从下向上画。而后再画倒数第二面墙的积木,… ,最后画第一面墙的积木。

最后输出整个画布K行L列上的所有字符。

【题解代码】

解法1:

#include<bits/stdc++.h>
using namespace std;
#define N 105
#define M 405
int h[N][N];//a[i][j]:x轴第i位置,y轴第j位置的积木高度 
char mp[M][M];
//co:打表得到的一个积木块的图像 
char co[10][10] = {
    
    //将积木块纵向颠倒存储,这样就是以左下角为坐标中心 
	"+---+..",
	"|   |/.",
	"|   | +",
	"+---+ |",
	"./   /|",
	"..+---+", 
};
void draw(int x, int y, int z)
{
    
    
	int a = 1+(z-1)*3+(y-1)*2, b = 1+(x-1)*4+(y-1)*2;//积木左下角在画布中的位置为(a, b)
	for(int i = 0; i < 6; ++i)//一个积木是6行7列的图像 从坐下角看(i,j) 
		for(int j = 0; j < 7; ++j)
			if(co[i][j] != '.')//如果是'.',则不覆盖原字符   
				mp[a+i][b+j] = co[i][j];//否则(a+i, b+j)位置覆盖为co[i][j] 
}
int main()
{
    
    
	int m, n, k = 0, l;//画布大小:横向l纵向k的区域
	cin >> m >> n;
	for(int i = 1; i <= m; ++i)
		for(int j = 1; j <= n; ++j)
			cin >> h[j][m-i+1];//输入矩阵中的第i行j列表示的格子,为三维坐标系中x轴方向第j位置,y轴方向第m-i+1位置。
	l = 7+(n-1)*4+(m-1)*2;
	for(int i = 1; i <= n; ++i)//x轴方向有n个 
		for(int j = 1; j <= m; ++j)//y轴方向有m个 
			k = max(k, 6+(h[i][j]-1)*3+(j-1)*2);
	for(int i = 1; i <= k; ++i)//画布大小为横向l纵向k的区域,都设为'.' 
		for(int j = 1; j <= l; ++j)
			mp[i][j] = '.';
	for(int y = m; y >= 1; --y)//从后向前 
		for(int x = 1; x <= n; ++x)//从左向右 
			for(int z = 1; z <= h[x][y]; ++z)//从下向上 
				draw(x, y, z);
	for(int i = k; i >= 1; --i)//输出画布(画布以坐下角为(1,1),而输出时要从左上角输出) 
	{
    
    
		for(int j = 1; j <= l; ++j)
			cout << mp[i][j];
		cout << endl;
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/lq1990717/article/details/125919647