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;
}

 

posted @ 2024-05-28 13:22  五月江城  阅读(59)  评论(0编辑  收藏  举报