洛谷P1058 [NOIP2008 普及组] 立体图

发布时间 2023-09-24 17:04:03作者: httony

写在前面

题解更新较少,请勿嗔怪。
本文粗鄙而简陋,要获得更好的阅读体验,请移步https://www.luogu.com.cn/problem/solution/P1058
NOIp普及组2008的第四题,题目网站https://www.luogu.com.cn/problem/P1058

关于题目

[NOIP2008 普及组] 立体图

题目描述

小渊是个聪明的孩子,他经常会给周围的小朋友们讲些自己认为有趣的内容。最近,他准备给小朋友们讲解立体图,请你帮他画出立体图。

小渊有一块面积为 \(m \times n\) 的矩形区域,上面有 \(m \times n\) 个边长为 \(1\) 的格子,每个格子上堆了一些同样大小的积木(积木的长宽高都是 \(1\)),小渊想请你打印出这些格子的立体图。我们定义每个积木为如下格式,并且不会做任何翻转旋转,只会严格以这一种形式摆放:

\[\def\arraystretch{1e-10} \begin{aligned} &\verb! +---+!\\ &\verb! / /|!\\ &\verb!+---+ |!\quad\textsf{高}\\ &\verb!| | +!\\ &\verb!| |/ !\quad\textsf{宽}\\ &\verb!+---+ !\\ & \quad\textsf{长} \end{aligned}\]

每个顶点用 \(1\) 个加号 + 表示,长用 \(3\)- 表示,宽用 \(1\)/,高用两个 | 表示。字符 +-/| 的 ASCII 码分别为 \(43\)\(45\)\(47\)\(124\)。字符 .(ASCII 码 \(46\))需要作为背景输出,即立体图里的空白部分需要用 . 来代替。立体图的画法如下面的规则:

若两块积木左右相邻,图示为:

\[\def\arraystretch{1e-10} \begin{aligned} \verb!..+---+---+!\\ \verb!./ / /|!\\ \verb!+---+---+ |!\\ \verb!| | | +!\\ \verb!| | |/.!\\ \verb!+---+---+..!\\ \end{aligned} \]

若两块积木上下相邻,图示为:

\[\def\arraystretch{1e-10} \begin{aligned} \verb!..+---+!\\ \verb!./ /|!\\ \verb!+---+ |!\\ \verb!| | +!\\ \verb!| |/|!\\ \verb!+---+ |!\\ \verb!| | +!\\ \verb!| |/.!\\ \verb!+---+..!\\ \end{aligned} \]

若两块积木前后相邻,图示为:

\[\def\arraystretch{1e-10} \begin{aligned} \verb!....+---+!\\ \verb!.../ /|!\\ \verb!..+---+ |!\\ \verb!./ /| +!\\ \verb!+---+ |/.!\\ \verb!| | +..!\\ \verb!| |/...!\\ \verb!+---+....!\\ \end{aligned} \]

立体图中,定义位于第 \((m,1)\) 的格子(即第 \(m\) 行第 \(1\) 列的格子)上面自底向上的第一块积木(即最下面的一块积木)的左下角顶点为整张图最左下角的点。

输入格式

第一行有用空格隔开的\(2\)个整数 \(m\)\(n\),表示有 \(m \times n\) 个格子 \((1 \le m,n \le 50)\)

接下来的 \(m\) 行,是一个 \(m \times n\) 的矩阵,每行有 \(n\) 个用空格隔开的整数,其中第 \(i\) 行第 \(j\) 列上的整数表示第 \(i\) 行第 \(j\) 列的格子上摞有多少个积木($1 \le $ 每个格子上的积木数 $ \le 100$)。

输出格式

输出包含题目要求的立体图,是一个 \(K\)\(L\) 列的字符串矩阵,其中 \(K\)\(L\) 表示最少需要 \(K\)\(L\) 列才能按规定输出立体图。

样例 #1

样例输入 #1

3 4
2 2 1 2
2 2 1 1
3 2 1 2

样例输出 #1

......+---+---+...+---+
..+---+  /   /|../   /|
./   /|-+---+ |.+---+ |
+---+ |/   /| +-|   | +
|   | +---+ |/+---+ |/|
|   |/   /| +/   /|-+ |
+---+---+ |/+---+ |/| +
|   |   | +-|   | + |/.
|   |   |/  |   |/| +..
+---+---+---+---+ |/...
|   |   |   |   | +....
|   |   |   |   |/.....
+---+---+---+---+......

提示

NOIP2008普及组第四题

关于思路

有思路吗?
很明显没有
其实很简单,只要用一个二维字符数组存储下其形状即可。
怎么办到?
不会!
很好办,要做的,只不过是把各个正方体倒序存储下来(这样一来,遮挡的可直接覆盖),然后就可以了!(~ ̄▽ ̄)~ (当然,这需要打个小小的表)
诶,那"."怎么办?
简单!只要判断一下是否数组当前位置被修改过就好了啦!٩(๑>◡<๑)۶
如果没修改过,那这里一定是".",因为所有正方体的构成字符都遍历了一遍了嘛!
那要怎么实现只遍历正方体?
用个数组存存不就行了!
好吧,咱们,上代码?

步骤进行时

#include<cstdio>
using namespace std;
int m,n,a[1001][1001],maxx,maxy,z[6]={2,1,0,0,0,0},s[6]={6,6,6,6,5,4};
char c[1001][1001],c1[10][10]={
"  +---+",
" /   /|",
"+---+ |",
"|   | +",
"|   |/",
"+---+"};//打表( ̄▽ ̄)/

这里变量m,n就是图的规模,maxx,maxy是用来存储字符数组大小的,sz则用来控制遍历正方体构成。a是为高度,c是一直说的存答案的字符数组,c1用来打表。

void fg(int x,int y){//x,y为当前在字符数组的位置(左下角坐标)
	for(int i=5;i>=0;i--)//立方体需要倒过来存入数组 
		for(int j=z[i];j<=s[i];j++){//分别是这一行的宽度 
			c[5-i+x][j+y]=c1[i][j];//修改。有重叠会自动覆盖
			if(5-i+x>maxx) maxx=5-i+x;
			if(j+y>maxy) maxy=j+y;//分别记录最大长宽 
		}
}

用来求答案的函数(最关键的一步!)

int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
		for(int j=0;j<m;j++)
			scanf("%d",&a[i][j]);
	for(int i=1;i<=n;i++)//层数 
		for(int j=0;j<m;j++)//列循环 
			for(int k=0;k<a[i][j];k++)//高度 
				fg((n-i)*2+1+3*k,(n-i)*2+1+4*j);//表示立方体左下角位置 
	for(int i=maxx;i>=1;i--){//因为倒序运算,所以也要倒序输出(maxx和maxy的用处)  
		for(int j=1;j<=maxy;j++)
			if(c[i][j]=='\0') printf(".");//为'\0',即未修改,也就是非正方体部分,要输出"."。 
			else printf("%c",c[i][j]);
		printf("\n");
	}
	return 0;
}

主函数

完整代码

#include<cstdio>
using namespace std;
int m,n,a[1001][1001],maxx,maxy,z[6]={2,1,0,0,0,0},s[6]={6,6,6,6,5,4};
char c[1001][1001],c1[10][10]={
"  +---+",
" /   /|",
"+---+ |",
"|   | +",
"|   |/",
"+---+"};
void fg(int x,int y){
	for(int i=5;i>=0;i--)
		for(int j=z[i];j<=s[i];j++){
			c[5-i+x][j+y]=c1[i][j];
			if(5-i+x>maxx) maxx=5-i+x;
			if(j+y>maxy) maxy=j+y;
		}
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
		for(int j=0;j<m;j++)
			scanf("%d",&a[i][j]);
	for(int i=1;i<=n;i++)
		for(int j=0;j<m;j++)
			for(int k=0;k<a[i][j];k++)
				fg((n-i)*2+1+3*k,(n-i)*2+1+4*j);
	for(int i=maxx;i>=1;i--){
		for(int j=1;j<=maxy;j++)
			if(c[i][j]=='\0') printf("."); 
			else printf("%c",c[i][j]);
		printf("\n");
	}
	return 0;
}

请注意

  • 要倒序存储,否则会造成覆盖失败导致乱象!(不要问我怎么知道的 ヽ(。>д<)p)
  • maxxmaxy很有用,循环时不用不行。
  • 字符不同于字符串,空的不是''!
  • 如果你拒绝使用zs,你的输出将不会有.

复杂度

由于一些特殊原因,无法把结果呈现给大家,在此附上网址:
https://www.luogu.com.cn/record/125943708
时间复杂度很低了,但空间稍微有点高(开了那么大数组嘛)

写在最后

没什么好写的了,只能说祝大家考试顺利!

THE END~