1. 程式人生 > >pat乙級1027. 列印沙漏(20)——二維陣列輸出和直接輸出兩種方法實現

pat乙級1027. 列印沙漏(20)——二維陣列輸出和直接輸出兩種方法實現

1027. 列印沙漏(20)

時間限制200 ms
記憶體限制65536 kB
程式碼長度限制8000 B
判題程式Standard作者CHEN, Yue

本題要求你寫個程式把給定的符號列印成沙漏的形狀。例如給定17個“*”,要求按下列格式列印

*****
 ***
  *
 ***
*****

所謂“沙漏形狀”,是指每行輸出奇數個符號;各行符號中心對齊;相鄰兩行符號數差2;符號數先從大到小順序遞減到1,再從小到大順序遞增;首尾符號數相等。

給定任意N個符號,不一定能正好組成一個沙漏。要求打印出的沙漏能用掉儘可能多的符號。

輸入格式:

輸入在一行給出1個正整數N(<=1000)和一個符號,中間以空格分隔。

輸出格式:

首先打印出由給定符號組成的最大的沙漏形狀,最後在一行中輸出剩下沒用掉的符號數。

輸入樣例:
19 *
輸出樣例:
*****
 ***
  *
 ***
*****
2

演算法設計:

這是一道找規律的題目,漏斗上下對稱,如果將漏斗的上半部分的倒三角的行數設定為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,可定義一個二維陣列來儲存這個漏斗,二維陣列的定義為:

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

總結:

這是一道很好的題目,既需要用到一些數學知識,又需要有一定的字元輸出的處理能力。以上兩種方法都是輸出字元圖形的題目常用方法,建議讀者藉此題仔細體會這兩種方法的區別,最好自己能夠熟練掌握並獨立實現這兩種方法。如果能做到這一點,輸出字元圖形的題目基本就沒有什麼問題了。