1. 程式人生 > >PTAL1-054 福到了(15 分)演算法詳解與坑點分析

PTAL1-054 福到了(15 分)演算法詳解與坑點分析

“福”字倒著貼,寓意“福到”。不論到底算不算民俗,本題且請你編寫程式,把各種漢字倒過來輸出。這裡要處理的每個漢字是由一個 N x N 的網格組成的,網格中的元素或者為字元“@”或者為空格。而倒過來的漢字所用的字元由裁判指定。
輸入格式:
輸入在第一行中給出倒過來的漢字所用的字元、以及網格的規模 N (不超過100的正整數),其間以 1 個空格分隔;隨後 N 行,每行給出 N 個字元,或者為“@”或者為空格。
輸出格式:
輸出倒置的網格,如樣例所示。但是,如果這個字正過來倒過去是一樣的,就先輸出“bu yong dao le”,然後再用輸入指定的字元將其輸出。
輸入樣例 1:
$ 9
 @  @@@@@
@@@  @@@ 
 @   @ @ 
@@@  @@@ 
@@@ @@@@@
@@@ @ @ @
@@@ @@@@@
 @  @ @ @
 @  @@@@@
輸出樣例 1:
$$$$$  $ 
$ $ $  $ 
$$$$$ $$$
$ $ $ $$$
$$$$$ $$$
 $$$  $$$
 $ $   $ 
 $$$  $$$
$$$$$  $ 
輸入樣例 2:
& 3
@@@
 @ 
@@@
輸出樣例 2:
bu yong dao le
&&&
 & 

&&&

通過本題,本人知道了演算法與最終實現的差距有時還是挺大的,先講演算法,本題涉及的主要演算法不難,一共兩個:

1.倒置輸出

通過仔細觀察規律,不難發現其實就是對原來正序輸出的二重迴圈的迴圈變數稍作修改,只要將迴圈變數的初始值改為N-1,變數的下界改為0即可,即從圖形的右下角,從右往左,輸出完一行,再輸出上一行,直到第一行輸出完畢。

2.判定是否需要倒置

如果你倒置輸出的演算法原理搞懂了,你會知道哪兩個字元是需要比較的,比如最簡單的情況,第一行第一個字元和右下角的字元,即最後一行從右往左數第1個字元是需要比較的,不妨將兩個字元的表示寫為A[0][0]和A[N-1][N-1],接著第一行第二個字元和最後一行從右往左數第二個字元是需要比較的,即A[0][1]和A[N-1][N-2]是需要比較的,這裡的行數沒有變,變的是列數。剛開始的0對應N-1,接著是1對應N-2,不難得出,如果列數為j,則對應的列數為N-1-j。需要注意的是需要換行比較的時候,行數就需要變化,即原字元所在行數增加,待比較字元所在行數減少,其中的規律也不難得出,如果行數為i,則對應的行數為N-i-1。至此,我們還需要一個判定是否需要倒置的標識變數,不妨設為flag,假設它的初值是1,表示不用倒,則你可以寫在外迴圈的判定條件中,如果不用倒,則繼續比較字元。一旦兩個字元不相等,則置flag=0,跳出內迴圈,外迴圈判定flag不為1,結束整個迴圈。

有了核心演算法還不夠,你還需要實現,而從演算法設計完成到實現要經過一些過程,其影響結果的主要過程就是IO格式,而此題的坑點就是:輸入存在空格和換行符的情況,以下是坑點的具體分析:

如果你用C++的IO方式,在鍵入字元時,如果使用了getline函式,那麼你可以不去關心有換行符的情況。如果你用C的IO方式,那麼你就必須在每次需要換行時處理換行的情況,即你需要在每次需要換行時使用getchar來接收換行符。

但無論是C還是C++,抑或別的語言,應該都需要處理在開頭鍵入數字後的換行符,這個換行符會影響接下來字元的輸入,C或C++可以用getchar來接收換行符。如果你用C的IO方式,你可能在接收字元的時候不去一個個地接收字元,而是用gets函式一次接收一行字元,但在PTA這個平臺上是不允許的,這是一個PTA隱藏的公共坑點,至於原因,我之前有問過相關負責人,gets是不安全的函式,已經禁用了。那麼,C語言使用者總共需要3個getchar,一個是來接收一開始輸入數字後的換行符,一個是用來接收每次鍵入的字元,還有一個是接收每次需要換行時輸入的換行符。C++如果使用getline那麼只需要一個getchar。

有了核心演算法與坑點的處理手段,如果沒有特殊情況的話,你已經能AC了,以下是具體程式碼:

#include<iostream>
using namespace std;
int main()
{
	int N;
	char c;
	cin>>c>>N;
	getchar();//注意!接收換行符,以免留在緩衝區被下一次getchar讀取 
	char fu[100][100];
	for(int i=0;i<N;i++)
	{
		for(int j=0;j<N;j++)
		{
			fu[i][j]=getchar();//使用getchar讀入任意字元 
			if(fu[i][j]!=' ')//如果不為空格,修改字元 
			fu[i][j]=c;
		}
		getchar();//注意!接收換行符,以免留在緩衝區被下一次getchar讀取 
	}
	int flag=1;//定義標識,初始化為1,代表不用倒
	for(int i=0;i<N&&flag;i++)//如果不用倒,則繼續 
	for(int j=0;j<N;j++)
	if(fu[i][j]!=fu[N-i-1][N-1-j])//如果當前字元不等於對應字元 
	{
		flag=0;//說明需要倒 
		break;
	}
	if(flag)
	cout<<"bu yong dao le\n";
	for(int i=N-1;i>=0;i--)
	{
		for(int j=N-1;j>=0;j--)//i,j的初始值和結束條件都改變,遞減,輸出 
		cout<<fu[i][j];
		cout<<'\n';
	}
	return 0;
}