《程式設計之美》的筆記 1 2 中國象棋將帥問題

2021-07-22 21:32:12 字數 4020 閱讀 7890

conndots (發現更多的生活。)

在讀程式設計之美

引子問題:中國象棋將帥問題:
在一把象棋的殘局中,象棋雙方的將帥不可以相見,即不可以在中間沒有其他棋子的情況下在同一列出現。而將、帥各被限制在己方的3*3的格仔中運動。相信大家都非常熟悉象棋的玩法吧,這裡就不詳細說明遊戲規則了。

用a、b代表將和帥,請寫出乙個程式,輸出a、b所有合法的位置。要求在**中只能用乙個變數。

分析與解法:這個問題的解法並不複雜。遍歷a的所有位置 遍歷b的所有位置 如果a的位置和b的位置在同一列 輸出結果 否則 繼續尋找地圖可以用0-8表示a或b可能的9個位置 0------1------2 3------4------5 6------7------8關鍵問題在於只使用乙個變數來表示a和b的位置。所以可以使用位運算來解決。乙個無符號字元型別的長度是1位元組,也就是8位,8位可以表示2^8=256個值,對於a、b的9個位置來說足夠。可以用前4位來表示a的位置情況,後4位表示b的位置情況。而4位可以表示16個數也足夠表示a、b的位置情況了。通過位運算可以對a、b的位置進行讀取和修改。幾種基本的位運算: (1)& 按位與運算 (2)| 按位或運算 "與"和"或"就不用說了吧 (3)^ 按位異或運算 相同為假,不同為真 (4)~ 按位取反 一元運算子 (5)<< 按位左移 如 0000 0111 << 2 = 0001 1100,將此數左移兩位相當於將此數擴大兩倍。 (6)>> 按位右移 如 0001 1000 >> 2 = 0000 0110,將此數右移兩位相當於將此數縮小兩倍。 令lmask為1111 0000,另任意乙個1位元組的字元型變數與其做與運算,結果右移四位,便可得到此變數的高四位的值。example, 0110 1011&1111 0000= 0110 0000 >> 4 = 0000 0110同理,令rmask為0000 1111,即可得到它低四位的值。ex. 0110 1011& 0000 1111= 0000 1011設定1位元組字元型變數,比如對高四位進行設定,先將變數與rmask相與,將要修改的變數左移四位後於前一結果進行「異或」或「或運算」。ex.將0110 1011高四位設定為1001. 0110 1011& 0000 1111= 0000 1011 0000 1001 << 4 = 1001 0000^ 1001 0000= 1001 1011同樣的方法設定低四位的值。**:

#include

#define half_bits_length 4

//一半位元組的長度

#define fullmask 255

//即1111 1111

#define

lmask

(fullmask << half_bits_length)

//即1111 0000

#define

rmask

(fullmask >> half_bits_length)

//即0000 1111

#define

rset

(b, n)

(b =

(lmask & b)

^ n)

//設定低四位

#define

lset

(b, n)

(b =

(rmask & b)

^(n << half_bits_length))

//設定高四位

#define

rget

(b)(rmask & b)

//得到低四位

#define

lget

(b)((lmask & b)

>> half_bits_length)

//得到高四位

#define gridw 3

//將帥活動範圍尺寸

#define byte unsigned

char

intmain

(void

)

這是個關於如何利用位運算解決問題的乙個簡單的運用,可以看到位運算合理地利用乙個變數解決象棋將帥問題。演算法本身很簡單,重點是位運算的應用。上還有兩個更簡潔的演算法:第乙個:

#include

#define byte unsigned

char

intmain

(void

)return0;

}

可以把變數i想象成乙個兩位九進製的變數,而i在計算機中儲存的值是i的十進位制表示。則i/9的計算機處理結果,即結果直接去掉小數點後部分的結果即是此九進製數的第二位,而i%9即是此九進製數的個位。本程式用此九進製數的第二位儲存a的位置,個位表示b的位置。最大值為88,即為十進位制的80.程式從十進位制的80,即九進製的88遍歷到十進位制的0,即九進製的0.將符合條件的位置全部輸出。第二個:

#include

intmain

(void)i;

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);

return0;

}

演算法與上面的如出一轍。其中unsigned char a:4表示結構體中a的位域只有4位,高位用作它用。只能在結構體裡使用,建議盡量少用,會破壞程式的移植性。當結構體中的元素的取值範圍很小時,可以將幾個欄位按位合成乙個欄位來表示,起到節省記憶體空間的作用。ex:

#include

intmain

(void)i;

i.a =15;

i.b =10;

printf

("%d\n"

,sizeof

(i));

}

將上面例子中的變數i的大小輸出,結果為1位元組。說明i.a和i.b各佔4位。結構體是c語言中的一種常用的自定義資料結構。看下面的例子:

#include

intmain

(void)i;

printf

("%d\n"

,sizeof

(i));

}

按理說結構體變數i的大小應該是sizeof(int)+sizeof(char),即5,而輸出顯示的結果為8。再看乙個例子:

#include

intmain

(void)i;

printf

("%d\n"

,sizeof

(i));

}

應該是6對吧?結果還是8.這是為什麼呢?這是因為在32位的作業系統上,作業系統組織資料是以32位(4個位元組)作為乙個標準,因此各種變數的size都一般都是4的倍數。而且結構體資料都是按照定義時所使用的順序存放的,因此在第乙個例子中儘管b變數只會占有乙個位元組,但是a + b = 5 > 4,因此第乙個4個位元組存放a,第二個4個位元組用於存放b,這樣實際上就浪費了3個位元組。在第二個例子中第二個4個位元組用來存放b和c。所以,在結構體中要注意結構體中的變數定義的順序,不同的順序可能會造成占用空間的不同。這在嵌入式程式設計等系統資源比較少的情況下尤為重要。比如如下兩種結構體:

#include

structmx;

structny;

intmain

(void

)

對於結構體m來說,x變數的大小為12,而y變數的大小為8.編譯器是按程式設計師在結構體中宣告變數的順序處理的。不當的順序會造成空間的浪費。讀者可以想到發生這樣情況的原因的。所以建議宣告結構體時,按照不同變數的型別,按占用空間的大小公升序或降序宣告會取得較好的空間占用。

《程式設計之美》 1 2 中國象棋將帥問題

下過中國象棋的朋友都知道。雙方的 將 和 帥 相隔遙遠,並且不能照面。在象棋殘局中,許多高手座利用這一規則走出精妙的殺招。假設棋盤上只有 將 和 帥 二子 為了下面敘述方便,我們約定用a 表示 將 b表示 帥 a b二子被限制在己方3x3 的格仔裡運動。每一步,a b分別可以橫向或縱向移 動一格。但...

程式設計之美 1 2中國象棋將帥問題

問題 現在有乙個象棋殘局,只剩下將a帥b,ab只能在3x3的格仔裡橫縱移動,而且ab不能照面。輸出a b的合法位置 要求 中只使用乙個變數 問題本身很簡單,難度在於只用乙個變數。解法一 位向量法,利用乙個8位byte儲存a b的位置,利用巨集抽象出對byte的操作 對l段賦值 對r段賦值 從byte...

1 2中國象棋將帥問題

include include 包含byte include using namespace std define half bit length 4 define fullmask 255 define lmask fullmask half bit length define rmask ful...