洛谷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是用来存储字符数组大小的,s和z则用来控制遍历正方体构成。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)
  • maxx和maxy很有用,循环时不用不行。
  • 字符不同于字符串,空的不是''!
  • 如果你拒绝使用z和s,你的输出将不会有.。

复杂度

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

写在最后

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

THE END~