【遊戲之樂】中國象棋 讓你學會位運算
任務難度:3
任務說明:
在中國象棋裡,當只剩下將帥(A和B代指將和帥)兩個棋子時 將帥不能處於同一列 要求輸出將帥的所有合法位置(將帥所在的九宮格從上到下從左至右編號為1~9)
例如:
合法位置:
A=1,B=2
不合法位置:
A=1,B=1
要求:只能使用一個變數
乍一看問題並不難,但是最後的要求是隻能使用一個變數,這也是好玩之處~~
先讓我們思考一下A和B的狀態共有多少種,九宮格的9個位置,即各有9種狀態,9需要用4bit來儲存,儲存AB兩種狀態則至少需要8bit,即一個BYTE
那麼主體思想就是我們通過某種迴圈方式,分別列舉AB狀態的所有值(1~9)當兩個狀態的%3即為兩個狀態的列數相同時,則不能輸出,否則輸出
接下來介紹三種方法,讓你學會位運算:
方法一:
假設我們開一個BYTE型的變數b,用左邊的4bit儲存B的狀態,用右邊的4bit儲存A的狀態,我們需要實現4種操作,即取出該變數的左邊四位或右邊四位,將該變數的左邊四位或右邊四位設定為n值(n取1~9)
取出右邊的值只需要將變數b與00001111(RMASK)相與即可,取出左邊四位的值只需要先將b與11110000(LMASK)相與,再右移四位即可
而將右邊的值改為n 就需要將左邊的值先保留下來 即將變數b與11110000相與,然後再和n異或即可
而將左邊的值改為n 就需要將右邊的值先保留下來,即將變數b與00001111相與,然後再和右移四位後的n異或即可
我們用四個巨集定義的函式實現上述功能,迴圈列舉的時候只需要先將b的左右置為1,然後每次多置1即可,程式碼如下:
#include <cstdio> #include <windows.h> /*解法一*/ #define HALF_BIT_LENGTH 4 //因為要頻繁使用左右移4位的操作,這裡把4巨集定義出來 #define FULLMASK 255 //即11111111 用於得出LMASK和RMASK #define LMASK (FULLMASK<<HALF_BIT_LENGTH) //將11111111左移4位 即得到11110000 #define RMASK (FULLMASK>>HALF_BIT_LENGTH) //即00001111 #define RGET(b) (b & RMASK) //得到右4位 #define LGET(b) ((b& LMASK)>>HALF_BIT_LENGTH) //得到左4位 #define RSET(b,n) (b= (b & LMASK)^n) //將右四位置為n #define LSET(b,n) (b= (b & RMASK)^(n<<HALF_BIT_LENGTH)) //將左四位置為n #define GRIDW 3 //由於求列數時需要對3取餘,這裡也巨集定義出來 int main() { unsigned char b; for (RSET(b,1);RGET(b)<=GRIDW*GRIDW;RSET(b,(RGET(b)+1))) {//列舉右四位從1到9的情況,即列舉A的9種狀態 for (LSET(b,1);LGET(b)<=GRIDW*GRIDW;LSET(b,(LGET(b)+1))) {//列舉左四位1到9的情況,即列舉B的9種狀態 if (RGET(b)%GRIDW!=LGET(b)%GRIDW)//列數不同則表示AB位置合法 { printf("A=%d, B=%d\n",RGET(b),LGET(b)); } } } return 0; }
是不是感覺很奇妙呢?這就是位運算的妙用,接下來介紹兩種更為簡便的方法
方法二:我們將AB的所有狀態排列組合,其實只有81種組合方式,我們直接定義一個BYTE變數i表示AB位置的排列方式,i=1表示A為1 B為1
用i/9+1表示A i%9+1表示B i的取值範圍為1~81 想一想這樣是不是涵蓋了A與B分別取1~9的81種情況
程式碼如下:
#include <cstdio>
#include <windows.h>
BYTE i=0;
int main()
{
for (;i<=81;i++)
{
if ( i/9%3 != i%9%3)
{
printf("A=%d, B=%d\n",i/9+1,i%9+1);
}
}
return 0;
接下來介紹最後一種方法 最簡單也最容易理解 直接建立一個大小為BYTE的struct變數 將BYTE的前4位和後4位分開直接設為兩個成員變數
程式碼如下:
#include <cstdio>
#include <windows.h>
struct
{
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;
怎麼樣 是不是很有意思呢?