CSP历年复赛题-P1058 [NOIP2008 普及组] 立体图
原题链接:https://www.luogu.com.cn/problem/P1058
题意解读:在m*n的平面上,输出若干个立方体,每一个格子可以有高度不同的多个立方体。
解题思路:
此题咋一看来,无从下手,仔细分析,其实一道模拟题。
如何模拟?我们一起来解决一下几个关键问题:
1、如何画图?
要在平面上输出图案,本质上就是将特定的字符填充到一个二维字符数组中,然后将合适的范围内的字符数组一一输出。
2、如何画方块?
可以提前用一个二维数组存储一个方块,例如:
char c[10][10] =
{
" +---+",
" / /|",
"+---+ |",
"| | +",
"| |/ ",
"+---+ "
};
然后,定义一个更大的画布:char m[2000][2000],将方块的内容复制到画布上。
如何确定画布的大小?m,n最大50,每个方块高6、宽7,高度最多100,画布长大概是50*7,宽大概是100*6,因此定义一个2000*2000的画布绰绰有余。
接下来,要解决的问题是,如何确定将方块画在什么位置?
我们可以设方块左下角的顶点放置在画布的(x, y)坐标上,为了简单起见,(x,y)初始放在画布正中央(1000,1000)
接下来,我们实现代码完成将一个方块画在画布上并输出。
//在画布m上以x,y为左下角,画一个方块c
void draw(int x, int y)
{
for(int j = 0; j < 5; j++) m[x][y + j] = c[5][j]; //复制最后一行,不包含空格
for(int j = 0; j < 6; j++) m[x-1][y + j] = c[4][j]; //复制第5行,不包含空格
for(int j = 0; j < 7; j++) m[x-2][y + j] = c[3][j]; //复制第4行
for(int j = 0; j < 7; j++) m[x-3][y + j] = c[2][j]; //复制第3行
for(int j = 1; j < 7; j++) m[x-4][y + j] = c[1][j]; //复制第2行,不包含空格
for(int j = 2; j < 7; j++) m[x-5][y + j] = c[0][j]; //复制第1行,不包含空格
}
补充完整将一个方块画在画布上,并输出的代码:
查看代码
#include <bits/stdc++.h>
using namespace std;
char c[10][10] =
{
" +---+",
" / /|",
"+---+ |",
"| | +",
"| |/ ",
"+---+ "
};
char m[2000][2000];
//在画布m上以x,y为左下角,画一个方块c
void draw(int x, int y)
{
for(int j = 0; j < 5; j++) m[x][y + j] = c[5][j]; //复制最后一行,不包含空格
for(int j = 0; j < 6; j++) m[x-1][y + j] = c[4][j]; //复制第5行,不包含空格
for(int j = 0; j < 7; j++) m[x-2][y + j] = c[3][j]; //复制第4行
for(int j = 0; j < 7; j++) m[x-3][y + j] = c[2][j]; //复制第3行
for(int j = 1; j < 7; j++) m[x-4][y + j] = c[1][j]; //复制第2行,不包含空格
for(int j = 2; j < 7; j++) m[x-5][y + j] = c[0][j]; //复制第1行,不包含空格
}
int main()
{
int x = 1000, y = 1000;
draw(x, y);
for(int i = x - 30; i <= x; i++)
{
for(int j = y; j <= y + 30; j++)
{
if(m[i][j] == 0) cout << ".";
else cout << m[i][j];
}
cout << endl;
}
}
输出结果
...............................
...............................
...............................
...............................
...............................
...............................
...............................
...............................
...............................
...............................
...............................
...............................
...............................
...............................
...............................
...............................
...............................
...............................
...............................
...............................
...............................
...............................
...............................
...............................
...............................
..+---+........................
./ /|........................
+---+ |........................
| | +........................
| |/.........................
+---+..........................
3、如何画多个方块在一起的情况?
由于根据题意,方块可以上下相邻、左右相邻、前后相邻,且相邻之后的方块会出现覆盖的效果,因此不难想象
对于上下相邻,必须从下到上画方块
对于左右相邻,必须从左到右画方块
对于前后相邻,必须从前到后画方块
先验证上下相邻的情况,先在(x,y)处画下面的方块,那么其上面一个方块的坐标会变成(x-3,y)
代码验证:
查看代码
#include <bits/stdc++.h>
using namespace std;
char c[10][10] =
{
" +---+",
" / /|",
"+---+ |",
"| | +",
"| |/ ",
"+---+ "
};
char m[2000][2000];
//在画布m上以x,y为左下角,画一个方块c
void draw(int x, int y)
{
for(int j = 0; j < 5; j++) m[x][y + j] = c[5][j]; //复制最后一行,不包含空格
for(int j = 0; j < 6; j++) m[x-1][y + j] = c[4][j]; //复制第5行,不包含空格
for(int j = 0; j < 7; j++) m[x-2][y + j] = c[3][j]; //复制第4行
for(int j = 0; j < 7; j++) m[x-3][y + j] = c[2][j]; //复制第3行
for(int j = 1; j < 7; j++) m[x-4][y + j] = c[1][j]; //复制第2行,不包含空格
for(int j = 2; j < 7; j++) m[x-5][y + j] = c[0][j]; //复制第1行,不包含空格
}
int main()
{
int x = 1000, y = 1000;
draw(x, y); //x,y处画方块
draw(x - 3, y); //上面再画一个方块
for(int i = x - 30; i <= x; i++)
{
for(int j = y; j <= y + 30; j++)
{
if(m[i][j] == 0) cout << ".";
else cout << m[i][j];
}
cout << endl;
}
}
输出结果
...............................
...............................
...............................
...............................
...............................
...............................
...............................
...............................
...............................
...............................
...............................
...............................
...............................
...............................
...............................
...............................
...............................
...............................
...............................
...............................
...............................
...............................
..+---+........................
./.../|........................
+---+.|........................
|.+-|-+........................
|/..|/|........................
+---+.|........................
|...|.+........................
|...|/.........................
+---+..........................
再验证左右相邻的情况,先在(x,y)处画左边的方块,那么其右边一个方块的坐标会变成(x,y+4)
代码验证:
查看代码
#include <bits/stdc++.h>
using namespace std;
char c[10][10] =
{
" +---+",
" / /|",
"+---+ |",
"| | +",
"| |/ ",
"+---+ "
};
char m[2000][2000];
//在画布m上以x,y为左下角,画一个方块c
void draw(int x, int y)
{
for(int j = 0; j < 5; j++) m[x][y + j] = c[5][j]; //复制最后一行,不包含空格
for(int j = 0; j < 6; j++) m[x-1][y + j] = c[4][j]; //复制第5行,不包含空格
for(int j = 0; j < 7; j++) m[x-2][y + j] = c[3][j]; //复制第4行
for(int j = 0; j < 7; j++) m[x-3][y + j] = c[2][j]; //复制第3行
for(int j = 1; j < 7; j++) m[x-4][y + j] = c[1][j]; //复制第2行,不包含空格
for(int j = 2; j < 7; j++) m[x-5][y + j] = c[0][j]; //复制第1行,不包含空格
}
int main()
{
int x = 1000, y = 1000;
draw(x, y); //x,y处画方块
draw(x - 3, y); //上面再画一个方块
draw(x, y + 4); //右边再画一个方块
for(int i = x - 30; i <= x; i++)
{
for(int j = y; j <= y + 30; j++)
{
if(m[i][j] == 0) cout << ".";
else cout << m[i][j];
}
cout << endl;
}
}
输出结果
...............................
...............................
...............................
...............................
...............................
...............................
...............................
...............................
...............................
...............................
...............................
...............................
...............................
...............................
...............................
...............................
...............................
...............................
...............................
...............................
...............................
...............................
..+---+........................
./.../|........................
+---+.|........................
|.+-|-+---+....................
|/..|/|../|....................
+---+---+.|....................
|...|.+.|.+....................
|...|/..|/.....................
+---+---+......................
再验证前后相邻的情况,先在(x,y)处画后面的方块,那么其前面一个方块的坐标会变成(x+2,y-2)
查看代码
#include <bits/stdc++.h>
using namespace std;
char c[10][10] =
{
" +---+",
" / /|",
"+---+ |",
"| | +",
"| |/ ",
"+---+ "
};
char m[2000][2000];
//在画布m上以x,y为左下角,画一个方块c
void draw(int x, int y)
{
for(int j = 0; j < 5; j++) m[x][y + j] = c[5][j]; //复制最后一行,不包含空格
for(int j = 0; j < 6; j++) m[x-1][y + j] = c[4][j]; //复制第5行,不包含空格
for(int j = 0; j < 7; j++) m[x-2][y + j] = c[3][j]; //复制第4行
for(int j = 0; j < 7; j++) m[x-3][y + j] = c[2][j]; //复制第3行
for(int j = 1; j < 7; j++) m[x-4][y + j] = c[1][j]; //复制第2行,不包含空格
for(int j = 2; j < 7; j++) m[x-5][y + j] = c[0][j]; //复制第1行,不包含空格
}
int main()
{
int x = 1000, y = 1000;
draw(x, y); //x,y处画方块
draw(x - 3, y); //上面再画一个方块
draw(x, y + 4); //右边再画一个方块
x += 2, y -= 2; //注意在前面画方块时,增加了x,为便于后面输出,要先修改x的值
draw(x, y); //前面再画一个方块
for(int i = x - 30; i <= x; i++)
{
for(int j = y; j <= y + 30; j++)
{
if(m[i][j] == 0) cout << ".";
else cout << m[i][j];
}
cout << endl;
}
}
输出结果
...............................
...............................
...............................
...............................
...............................
...............................
...............................
...............................
...............................
...............................
...............................
...............................
...............................
...............................
...............................
...............................
...............................
...............................
...............................
...............................
....+---+......................
.../.../|......................
..+---+.|......................
..|.+-|-+---+..................
..|/..|/|../|..................
..+---+---+.|..................
./|../|.+.|.+..................
+---+.|/..|/...................
|.+-|-+---+....................
|...|/.........................
+---+..........................
4、如何画出题目要求的图案?
根据题意,在一个m*n的区间,每个格子有高度不同的方块
可以从最后一排左边开始,从后到前枚举每一行,从左往右枚举每一列,从下往上枚举每一层依次画出各个方块
根据样例输入编写代码:
查看代码
#include <bits/stdc++.h>
using namespace std;
char c[10][10] =
{
" +---+",
" / /|",
"+---+ |",
"| | +",
"| |/ ",
"+---+ "
};
char m[2000][2000];
//在画布m上以x,y为左下角,画一个方块c
void draw(int x, int y)
{
for(int j = 0; j < 5; j++) m[x-0][y + j] = c[5][j]; //复制最后一行,不包含空格
for(int j = 0; j < 6; j++) m[x-1][y + j] = c[4][j]; //复制第5行,不包含空格
for(int j = 0; j < 7; j++) m[x-2][y + j] = c[3][j]; //复制第4行
for(int j = 0; j < 7; j++) m[x-3][y + j] = c[2][j]; //复制第3行
for(int j = 1; j < 7; j++) m[x-4][y + j] = c[1][j]; //复制第2行,不包含空格
for(int j = 2; j < 7; j++) m[x-5][y + j] = c[0][j]; //复制第1行,不包含空格
}
int main()
{
int row, col, height;
cin >> row >> col;
int x = 1000, y = 1000;
for(int i = 0; i < row; i++) //从后往前
{
x += 2, y -= 2;
for(int j = 0; j < col; j++) //从左往右
{
cin >> height; //每个格子的高度
for(int k = 0; k < height; k++) //从下往上
{
draw(x - 3 * k, y + 4 * j);
}
}
}
for(int i = x - 30; i <= x; i++)
{
for(int j = y; j <= y + 30; j++)
{
if(m[i][j] == 0) cout << ".";
else cout << m[i][j];
}
cout << endl;
}
}
输出结果
...............................
...............................
...............................
...............................
...............................
...............................
...............................
...............................
...............................
...............................
...............................
...............................
...............................
...............................
...............................
...............................
...............................
...............................
......+---+---+...+---+........
..+---+ / /|../ /|........
./ /|-+---+ |.+---+ |........
+---+ |/ /| +-| | +........
| | +---+ |/+---+ |/|........
| |/ /| +/ /|-+ |........
+---+---+ |/+---+ |/| +........
| | | +-| | + |/.........
| | |/ | |/| +..........
+---+---+---+---+ |/...........
| | | | | +............
| | | | |/.............
+---+---+---+---+..............
有效结果已经和样例输出一致了!
5、如何确定输出的范围?
最后的问题,是如何只输出有效区域的内容,一个简单的方法是:枚举每一行每一列,记录非'.'的最小行、最大行,最小列,最大列,按行列输出即可。
100分代码:
#include <bits/stdc++.h>
using namespace std;
char c[10][10] =
{
" +---+",
" / /|",
"+---+ |",
"| | +",
"| |/ ",
"+---+ "
};
char m[2000][2000];
//在画布m上以x,y为左下角,画一个方块c
void draw(int x, int y)
{
for(int j = 0; j < 5; j++) m[x-0][y + j] = c[5][j]; //复制最后一行,不包含空格
for(int j = 0; j < 6; j++) m[x-1][y + j] = c[4][j]; //复制第5行,不包含空格
for(int j = 0; j < 7; j++) m[x-2][y + j] = c[3][j]; //复制第4行
for(int j = 0; j < 7; j++) m[x-3][y + j] = c[2][j]; //复制第3行
for(int j = 1; j < 7; j++) m[x-4][y + j] = c[1][j]; //复制第2行,不包含空格
for(int j = 2; j < 7; j++) m[x-5][y + j] = c[0][j]; //复制第1行,不包含空格
}
int main()
{
int row, col, height;
cin >> row >> col;
int x = 1000, y = 1000;
for(int i = 0; i < row; i++) //从后往前
{
x += 2, y -= 2;
for(int j = 0; j < col; j++) //从左往右
{
cin >> height; //每个格子的高度
for(int k = 0; k < height; k++) //从下往上
{
draw(x - 3 * k, y + 4 * j);
}
}
}
int minx = 2000, maxx = 0, miny = 2000, maxy = 0;
for(int i = 0; i < 2000; i++)
{
for(int j = 0; j < 2000; j++)
{
if(m[i][j] != 0)
{
minx = min(minx, i);
maxx = max(maxx, i);
miny = min(miny, j);
maxy = max(maxy, j);
}
}
}
for(int i = minx; i <= maxx; i++)
{
for(int j = miny; j <= maxy; j++)
{
if(m[i][j] == 0) cout << '.';
else cout << m[i][j];
}
cout << endl;
}
return 0;
}