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

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

题目描述

算法设计

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

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

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

第一种方法

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

char result[row][row];

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

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

很明显,整个二维数组关于第mid-1行上下对称的,根据这个性质我们可以从第mid-1行开始向上下两个方向同时填充字符c,这样填充mid次即可将整个二维数组填充满。可以定义一个辅助变量i(i>=0&&i<mid),则相应的填充的行号为mid-1-imid-1+i。由此确定了填充位置的行号,接着确定列号。

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

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

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

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

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

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

for(int i=0;i<mid;++i)
        for(int j=mid-1-i;j<mid+i;++j)
            result[mid-1-i][j]=result[mid-1+i][j]=c;

注意点

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

C++代码

#include<bits/stdc++.h>
using namespace std;
int main(){
    int N;
    char c;
    scanf("%d %c",&N,&c);
    //获取漏斗上半部分行数mid,总行数row
    int mid=(int)sqrt((N+1)/2*1.0);
    int row=2*mid-1;
    //定义二维数组
    char result[row][row];
    //初始化二维数组
    for(int i=0;i<row;++i)
        for(int j=0;j<row;++j)
            result[i][j]=' ';
    //填充二维数组
    for(int i=0;i<mid;++i)
        for(int j=mid-1-i;j<mid+i;++j)
            result[mid-1-i][j]=result[mid-1+i][j]=c;
    //输出二维数组
    for(int i=0;i<row;++i){
        bool output=true;
        for(int j=0;j<row;++j){
            if(result[i][j]==' '&&!output)
                break;
            if(result[i][j]!=' '){
                output=false;
            }
            printf("%c",result[i][j]);
        }
        printf("\n");
    }
    //输出剩余字符个数
    printf("%d",N-2*mid*mid+1);
    return 0;
}

第二种方法

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

(空白部分不予输出)

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

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

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

int space=0;
    for(int i=row;i>=1;i-=2){
        for(int j=0;j<space;++j)
            printf(" ");
        ++space;
        for(int j=0;j<i;++j)
            printf("%c",c);
        printf("\n");
    }

下半部分与此类似。

C++代码

#include<bits/stdc++.h>
using namespace std;
int main(){
    int N;
    char c;
    scanf("%d %c",&N,&c);
    //获取漏斗上半部分行数mid,总行数row
    int mid=(int)sqrt((N+1)/2*1.0);
    int row=2*mid-1;
    //输出上半部分
    int space=0;
    for(int i=row;i>=1;i-=2){
        for(int j=0;j<space;++j)
            printf(" ");
        ++space;
        for(int j=0;j<i;++j)
            printf("%c",c);
        printf("\n");
    }
    --space;
    //输出下半部分
    for(int i=3;i<=row;i+=2){
        --space;
        for(int j=0;j<space;++j)
            printf(" ");
        for(int j=0;j<i;++j)
            printf("%c",c);
        printf("\n");
    }
    //输出剩余字符个数
    printf("%d",N-2*mid*mid+1);
    return 0;
}

总结:

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

发布了526 篇原创文章 · 获赞 976 · 访问量 36万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 撸撸猫 设计师: 设计师小姐姐

分享到微信朋友圈

×

扫一扫,手机浏览