最近開始看編程之美這本書,里面有一道關於中國象棋將帥位置的簡單問題,如下圖所示,寫一個程序輸出將、帥的合法位置。
分析與解法
問題的本身並不復雜,只要把所有A、B 互相排斥的條件列舉出來就可以完成本題的要 求。由於本題要求只能使用一個變量,所以必須首先想清楚在寫代碼的時候,有哪些信息需 要存儲,並且盡量高效率地存儲信息。稍微思考一下,可以知道這個程序的大體框架是:
遍歷A的位置
遍歷B的位置
判斷A、B的位置組合是否滿足要求
如果滿足,則輸出
因此,需要存儲的是A、B 的位置信息,並且每次循環都要更新。為了能夠進行判斷, 首先需要創建一個邏輯的坐標系統,以便檢測 A 何時會面對 B。這里我們想到的方法是用 1~9的數字,按照行優先的順序來表示每個格點的位置。這樣,只需要用 模余運算就可以得到當前的列號,從而判斷A、B 是否互斥。
若題目要求只用一個變量,但是我們卻要存儲 A 和 B 兩個子的位置信息,該怎么辦呢?
可以先把已知變量類型列舉一下,然后做些分析。
對於bool類型,估計沒有辦法做任何擴展了,因為它只能表示true和false 兩個值;而 byte 或者 int 類型,它們能夠表達的信息則更多。事實上,對本題來說,每個子都只需要 9 個數字就可以表達它的全部位置。
一個8位的byte類型能夠表達28=256個值,所以用它來表示A、B的位置信息綽綽有余, 因此可以把這個字節的變量(設為b)分成兩部分。用前面的4 bit表示A的位置,用后面的 4 bit表示B的位置,那么4個bit可以表示16個數,這已經足夠了。
那么:如何使用bit級的運算將數據從這一byte變量的左邊和右邊分別存入和讀出呢?
大家容易想到的是對那個變量進行各種位運算,最后輸出結果。
但是其實C語言中還提供了一種存在於結構體中叫做位域的類型,因此程序就變得簡單多了。關於位域更多的用法規則,詳見:C位域
#include <stdio.h> struct bf{ unsigned char a:4; unsigned char b:4; }i; /*定義位域結構*/ int main() { for(i.a = 1; i.a <= 9; i.a++) { for(i.b = 1; i.b <= 9; i.b++) { if(i.a % 3 != i.b %3) printf("A = %d, B = %d\n", i.a, i.b); } } return 0; }
另一巧妙解法:
int main() { unsigned char i = 81; while(i--) { if(i/9%3 != i%9%3) printf("A = %d, B = %d\n", i/9+1, i%9+1); } return 0; }