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
首先要明確在樣例中mid=3,row=5,c='*'
第一種方法:
經過前面的敘述已經可以獲取整個漏斗的行數row,可定義一個二維陣列來儲存這個漏斗,二維陣列的定義為:
charresult[row][row];
首先將整個二維陣列均初始化為空格字元,接著向這個二維陣列中填充字元c,怎麼填充呢?我們可以先看一下最終應形成的二維陣列:
填充字元c需要獲取二維陣列中關於需要進行填充的位置的行號列號。
很明顯,整個二維陣列關於第mid-1行上下對稱的,根據這個性質我們可以從第mid-1行開始向上下兩個方向同時填充字元c,這樣填充mid次即可將整個二維陣列填充滿。可以定義一個輔助變數i(i>=0&&i<mid),則相應的填充的行號為mid-1-i和mid-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;
}
總結:
這是一道很好的題目,既需要用到一些數學知識,又需要有一定的字元輸出的處理能力。以上兩種方法都是輸出字元圖形的題目常用方法,建議讀者藉此題仔細體會這兩種方法的區別,最好自己能夠熟練掌握並獨立實現這兩種方法。如果能做到這一點,輸出字元圖形的題目基本就沒有什麼問題了。