写在前面
题解更新较少,请勿嗔怪。
本文粗鄙而简陋,要获得更好的阅读体验,请移步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\)),小渊想请你打印出这些格子的立体图。我们定义每个积木为如下格式,并且不会做任何翻转旋转,只会严格以这一种形式摆放:
每个顶点用 \(1\) 个加号 +
表示,长用 \(3\) 个 -
表示,宽用 \(1\) 个 /
,高用两个 |
表示。字符 +
,-
,/
,|
的 ASCII 码分别为 \(43\),\(45\),\(47\),\(124\)。字符 .
(ASCII 码 \(46\))需要作为背景输出,即立体图里的空白部分需要用 .
来代替。立体图的画法如下面的规则:
若两块积木左右相邻,图示为:
若两块积木上下相邻,图示为:
若两块积木前后相邻,图示为:
立体图中,定义位于第 \((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
时间复杂度很低了,但空间稍微有点高(开了那么大数组嘛)
写在最后
没什么好写的了,只能说祝大家考试顺利!