pat乙级1027. 打印沙漏(20)——二维数组输出和直接输出两种方法实现

欢迎访问我的pat乙级题解目录哦https://blog.csdn.net/richenyunqi/article/details/84981369

题目描述

pat乙级1027. 打印沙漏(20)——二维数组输出和直接输出两种方法实现_第1张图片

算法设计

对于图形输出类的题目,我们通常需要找出图形中的规律。漏斗上下对称,如果将漏斗的上半部分的倒三角的行数设置为k(例如题中所给样例k=3),形成漏斗的字符总数为M,由于行数逐行递减的字符数为2,则根据等差数列性质有:(1+k)*k=M;即k的平方=(M+1)/2,注意这里的M是恰好形成漏斗的符号个数。对于题目中所给的字符总数N来说,可以进行类似运算,只不过需要对(N+1)/2向下取整,那么剩余的字符个数也很好得到了,即为N-2*k*k+1 。

由此就可以求出漏斗的上半部分的行数,从而漏斗第一行的符号个数以及整个漏斗的行数row=2*k-1。假定填充漏斗的字符为c,至此,就可以开始打印整个漏斗了。打印漏斗有两种方法,第一种是定义一个char类型的二维数组,并在二维数组中进行一些操作将形成漏斗的符号存储起来,然后打印出这个二维数组;第二种是直接按要求打印。下面以样例为例一一介绍这两种方法。

首先要明确在样例中k=3,row=5,c='*'

第一种方法

经过前面的叙述已经可以获取整个漏斗的行数row,可定义一个二维数组来存储这个漏斗,二维数组的定义为:

//定义二维数组并将元素都初始化为空格字符
vector> ans(row, vector(row, ' '));

首先将整个二维数组均初始化为空格字符,接着向这个二维数组中填充字符c,怎么填充呢?我们可以先看一下最终应形成的二维数组:

pat乙级1027. 打印沙漏(20)——二维数组输出和直接输出两种方法实现_第2张图片

填充字符c需要获取二维数组中关于需要进行填充的位置的行号列号。

很明显,整个二维数组关于第k-1行上下对称的,根据这个性质我们可以从第k-1行开始向上下两个方向同时填充字符c,这样填充k次即可将整个二维数组填充满。可以定义一个辅助变量i(i>=0 and i

很明显,整个二维数组关于第k-1列左右对称的,根据这个性质我们可以从第k-1列开始向上下两个方向同时填充字符c。可以利用上面所说的辅助变量i,通过上面最终需形成的二维数组可知:

当i=0时,填充的行号为2,填充的列号左界为2,右界为2;

当i=1时,填充的行号为1,3,填充的列号左界为1,右界为3;

当i=2时,填充的行号为0,4,填充的列号左界为0,右界为4;

所以通过以上分析可知,列号的范围为[k-1-i,k-1+i]。注意是闭区间

综上,填充字符的整个代码为:

for (gg i = 0; i < k; ++i)
        for (gg j = k - 1 - i; j < k + i; ++j)
            ans[k - 1 - i][j] = ans[k - 1 + i][j] = ci;

注意点

输出二维数组,此时需要注意一点,题目要求行尾不能输出多余空格,例如第2行3,4列的两个空格字符是不能输出的!不然会有1,4两个测试点不能通过,所以不能简单的输出整个二维数组,要进行一些处理。

C++代码

#include 
using namespace std;
using gg = long long;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    gg ni;
    char ci;
    cin >> ni >> ci;
    //获取漏斗上半部分行数k,总行数row
    gg k = (gg)sqrt((ni + 1) / 2 * 1.0), row = 2 * k - 1;
    //定义二维数组并将元素都初始化为空格字符
    vector> ans(row, vector(row, ' '));
    //填充二维数组
    for (gg i = 0; i < k; ++i)
        for (gg j = k - 1 - i; j < k + i; ++j)
            ans[k - 1 - i][j] = ans[k - 1 + i][j] = ci;
    //输出二维数组
    for (gg i = 0; i < row; ++i) {
        bool output = true;
        for (gg j = 0; j < row; ++j) {
            if (ans[i][j] == ' ' and !output)
                break;
            if (ans[i][j] != ' ') {
                output = false;
            }
            cout << ans[i][j];
        }
        cout << '\n';
    }
    //输出剩余字符个数
    cout << ni - 2 * k * k + 1;
    return 0;
}

第二种方法

直接进行输出的话,要确定两个量,一是输出的空格字符的数量,一是输出的字符c的数量,可将整个漏斗分为上半部分和下班部分两部分进行输出,最后输出的图形如下所示:

pat乙级1027. 打印沙漏(20)——二维数组输出和直接输出两种方法实现_第3张图片

(空白部分不予输出)

对于上半部分(第0行到第2行),空格字符数量由0递增到2,递增步长为1;字符c数量由row递减到1,递减步长为2 。

对于下半部分(第3行到第4行),空格字符数量由1递减到0,递减步长为1;字符c数量由3递增到row,递增步长为2 。

可定义一个变量space表示空格字符数量,则输出上半部分的代码为:

gg space = 0;
for (gg i = row; i >= 1; i -= 2) {
    for (gg j = 0; j < space; ++j)
        cout << ' ';
    ++space;
    for (gg j = 0; j < i; ++j)
        cout << ci;
    cout << '\n';
}

下半部分与此类似。

C++代码

#include 
using namespace std;
using gg = long long;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    gg ni;
    char ci;
    cin >> ni >> ci;
    //获取漏斗上半部分行数mid,总行数row
    gg k = (gg)sqrt((ni + 1) / 2 * 1.0), row = 2 * k - 1;
    //输出上半部分
    gg space = 0;
    for (gg i = row; i >= 1; i -= 2) {
        for (gg j = 0; j < space; ++j)
            cout << ' ';
        ++space;
        for (gg j = 0; j < i; ++j)
            cout << ci;
        cout << '\n';
    }
    --space;
    //输出下半部分
    for (gg i = 3; i <= row; i += 2) {
        --space;
        for (gg j = 0; j < space; ++j)
            cout << ' ';
        for (gg j = 0; j < i; ++j)
            cout << ci;
        cout << '\n';
    }
    //输出剩余字符个数
    cout << ni - 2 * k * k + 1;
    return 0;
}

总结:

这是一道很好的题目,既需要用到一些数学知识,又需要有一定的字符输出的处理能力。以上两种方法都是输出字符图形的题目常用方法,建议读者借此题仔细体会这两种方法的区别,最好自己能够熟练掌握并独立实现这两种方法。如果能做到这一点,输出字符图形的题目基本就没有什么问题了。

你可能感兴趣的:(pat乙级)