首页 > 其他分享 >CSP历年复赛题-P1058 [NOIP2008 普及组] 立体图

CSP历年复赛题-P1058 [NOIP2008 普及组] 立体图

时间:2024-05-28 13:34:41浏览次数:16  
标签:............................... int +---+ 立体图 NOIP2008 ++ 复制 P1058 方块

原题链接: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;
}

 

标签:...............................,int,+---+,立体图,NOIP2008,++,复制,P1058,方块
From: https://www.cnblogs.com/jcwy/p/18217778

相关文章

  • CSP历年复赛题-P1057 [NOIP2008 普及组] 传球游戏
    原题链接:https://www.luogu.com.cn/problem/P1057题意解读:n个人围一圈,从1开始传球m次,每次可以往左或右传,计算球再次传给1的方案数。解题思路:求方案数,通常就是DP问题,此题DP状态并不难想,如果实在不会,也可以通过DFS暴搜得部分分。1、DFS60分代码:#include<bits/stdc++.h>using......
  • CSP历年复赛题-P1055 [NOIP2008 普及组] ISBN 号码
    原题链接:https://www.luogu.com.cn/problem/P1055题意解读:验证ISBN最后一位是否正确。解题思路:直接模拟,不多说,上代码。100分代码:#include<bits/stdc++.h>usingnamespacestd;intmain(){strings;cin>>s;intcode=0;intcnt=0;for(inti......
  • 洛谷[普及]:P1149 [NOIP2008 提高组] 火柴棒等式
    [NOIP2008提高组]火柴棒等式感谢题目提供者CCF_NOI题目描述给你n 根火柴棍,你可以拼出多少个形如A+B=C 的等式?等式中的A、B、C 是用火柴棍拼出的整数(若该数非零,则最高位不能是0)。用火柴棍拼数字 的拼法如图所示:注意:1.加号与等号各自需要两根火柴棍;2.如果,则......
  • P1057 [NOIP2008 普及组] 传球游戏
    链接:https://www.luogu.com.cn/problem/P1057思路:左手倒右手,建立递推方程建立初始参数:定义dp[j][k]是第k次,以j结尾的方法,就是传k次最后传到j的方法。那么状态转移方程:dp[j][k]=dp[next][k-1]+dp[before][k-1]。其中before是j的前一个元素(j-1);next是j的后一个元素j+1。同时要注......
  • P1155 [NOIP2008 提高组] 双栈排序
    P1155[NOIP2008提高组]双栈排序有思维的二分图染色题。对于“双”类的题目,我们通常分开考虑单个时的性质。对于一个栈,有一个基本的定理:若出现\(i<j<k\),有\(a_k<a_i<a_j\),那么一定不合法,即没有合法的出栈顺序使之有序。对于两个栈,我们相当于把序列分成两部分,使每部分之间......
  • P1149 [NOIP2008 提高组] 火柴棒等式
    P1149[NOIP2008提高组]火柴棒等式题目给你\(n\)根火柴棍,你可以拼出多少个形如\(A+B=C\)的等式?等式中的\(A\)、\(B\)、\(C\)是用火柴棍拼出的整数(若该数非零,则最高位不能是\(0\))。用火柴棍拼数字\(0\sim9\)的拼法如图所示:注意:加号与等号各自需要两根火柴棍;如果......
  • P1149 [NOIP2008 提高组] 火柴棒等式
    目描述给你 n 根火柴棍,你可以拼出多少个形如A+B=C 的等式?等式中的 A、B、C 是用火柴棍拼出的整数(若该数非零,则最高位不能是 00)。用火柴棍拼数字 0∼90∼9 的拼法如图所示:注意:加号与等号各自需要两根火柴棍;如果A=B,则+B=C 与B+A=C 视为不同的等式(≥0A,B,C≥......
  • 洛谷 P1006 [NOIP2008 提高组] 传纸条
    题意:传纸条,跟方格取数一样,但是两条路径不能有重复的。思路:还是一样的走,但是x1跟x2不能相等,包括现在跟上一个状态。总结:看了题解,发现题解大多数都是逻辑不正确的,更有离谱的是数组范围都不加特判,数组访问越界但是可以ac的情况,数据太烂了,放个自以为正确的思路吧,发现之前自己提交的......
  • P1149 [NOIP2008 提高组] 火柴棒等式
    题目链接:本题比较重要的点在于判断加数的范围,即枚举的范围大小。由于题目已知\(n\leqslant24\),且用数字\(1\)拼成的数尽可能大。由于\(1111+1=1112\)已经用了\(25\)根小棒,已经超过了题目\(24\)根小棒的数据范围,所以上界为\(1111\)。#include<cstdio>inta[10]=......
  • 【洛谷】 P1006 [NOIP2008提高组]传纸条
    题目描述小渊和小轩是好朋友也是同班同学,他们在一起总有谈不完的话题。一次素质拓展活动中,班上同学安排坐成一个 m 行 n 列的矩阵,而小渊和小轩被安排在矩阵对角线的两端,因此,他们就无法直接交谈了。幸运的是,他们可以通过传纸条来进行交流。纸条要经由许多同学传到对方手里,......