從算法學起C語言--八皇后(位運算算法)

轉載請註明出處,謝謝~

上一篇博客裏介紹了背景和說明,在此就不多複述了,而上一篇代碼使用三個一位數組表示的皇后位置的可用與否,此篇博客就用機器語言0和1表示皇后的放置位置。

首先補充知識:

假設:

int n =2 ; int u = 1;

那麼 u = (u<<n)-1;是多少??

首先,1<<2,用二進制是100(不是一百,而是二進制100),它在減去1就是011.所以 u = (u<<n) -1 = 0000 0000 0000 0011;

然後補充原碼補碼和反碼的運算:

首先,數的存儲是按照補碼的形式存儲的。所以二進制的運算要轉化成對應補碼之後再運算。假設n = 4,那麼它的二進制就是 0100 , 那麼它的原碼反碼和補碼都是 0100,這是正數,那麼-n的原碼反碼和補碼分別是多少呢?-4的原碼就是1100,最高位1表示符號,-4的反碼是1011,除符號位外其他位取反,-4的補碼是1100,就是它的反碼加1.這是負數的規則。

補充 |,&,~運算規則:

|運算:

1 | 1 = 1,1 | 0 = 1 ;0 | 1 = 1;0 | 0 = 0;可以這麼看,0爲假,1爲真,|爲或者,數學的運算規則,只有 假 或者 假 的時候才爲假,其他的都是真。

&運算:

1 & 1 = 1; 1 & 0 = 0 ; 0 & 1 = 0; 0 & 0 = 0;同樣可以用數學規則看,就是隻有 真 並且 真 的時候才爲真。

~運算:

這是取反運算,例如當二進制1011進行~運算後,它就變成了0100.

補充運算技巧,以下全是按照二進制數進行運算:

1.進行|運算可以找出多個數同時爲0的位置,如 1001 | 1010 ,那麼就是1011,可以知道只有倒數第三位爲0.

2.如果一個數 & 一個全爲1的二進制數,那麼得出的結果還是原數。如1111&1001 = 1001.

3.如果一個正數 & 它的相反數,那麼得到的是這個數從右邊開始第一個不爲0的數,其他位全爲0.如6是0110,那麼6 &(-6)= 0010

以上有書寫不對的地方,請忽略,我就是讓你明白個大概意思。。。

下面先把代碼列出:

void initQueen(void)
{
	int n = 2;
	int upperlim = 1;
	if ((n < 1) || (n > 32))                   
	{  
		printf(" 只能計算1-32之間\n");  
		exit(-1);  
	}
	upperlim = (upperlim << n) -1;
	queen(0,0,0,upperlim);
}

void queen(long raw ,long ld,long rd,int upperlim)
{
	if (raw != upperlim)
	{
		long pos = upperlim & ~(raw | ld | rd);
		while (pos)
		{
			long p = pos & -pos;
			pos -= p;
			queen(raw+p,(ld+p)<<1,(rd+p)>>1);
		}
	}else{
		sum++;
	}
}
然後我們逐行解釋代碼:

if ((n < 1) || (n > 32))                   
	{  
		printf(" 只能計算1-32之間\n");  
		exit(-1);  
	}
這一段是根據int型的數只有32位而做的限制。

upperlim = (upperlim << n) -1;
這就是補充規則裏的移位運算,n的值是根據幾皇后而定,這裏我們爲了說的方便用的是2皇后,那麼運算結果就是

upperlim = (0001 << 2) - 1 = 0011=11; 用結果中的從右開始的相鄰的1的個數來表示n皇后問題所用到的位數。

queen(0,0,0,upperlim);
開始執行程序,raw表示行,ld表示左斜線,rd表示右斜線。

if (raw != upperlim)
raw != upperlim,就是表示當前還未找到問題的解,如果找到了問題的一個解,那麼所有的列(不是行)應該是都放滿了皇后,那麼當前行就應該不能在放置皇后(不考慮斜線的情況下),也就是不在有0的存在。

long pos = upperlim & ~(raw | ld | rd);
這裏根據補充的運算技巧1,可以得知raw|ld|rd就是表示當前所有不爲1的位置,然後取反,即用1表示所有可以放置皇后的位置,upperlim是全1的二進制,&運算之後就是得到了pos表示一行上所有可以放置皇后的位置。

while (pos)
如果pos爲0(假)了,那麼就表示這一行上沒有可以放置皇后的位置了。

long p = pos & -pos;

根據運算技巧3,它就可以得到從右邊開始第一個不爲0的二進制,用p表示我們第一次取的放置皇后的位置。

pos -= p;
在pos中減去p(p的位置置爲0),即取出放置皇后的位置,回溯時如果pos中有其他位置是1,則表示還有其他位置可以放置皇后,繼續循環,如果回溯時pos全爲0,即pos=0,那麼就表示沒有位置可以放置皇后了,就跳出while循環

queen(raw+p,(ld+p)<<1,(rd+p)>>1);
繼續下一次遞歸的放置皇后。其中,raw+p,p爲當前放置皇后的位置,raw加上它raw的二進制就表示raw如果是0則可以放置皇后,如果是1則不可以。(在這裏用0表示可以,所以在循環體前用一個~取反來置換成用1表示)。(ld+p)<<1,這是左斜線,因爲左斜線佔用的位置是下一行的當前位置的左邊,所以這裏要左移1位表示,那裏不可以放置皇后,同理,右斜線要右移1位。這裏都是用1表示不可以放置皇后,用0表示可以。

else{
		sum++;
	}
當所有的行都放置了皇后,則raw == upperlim(raw每次二進制中都會把一個0變成1,當完成一次解後,它所對應的n個0應該都爲1,即等於了upperlim的值),那麼else裏就+1表示有了一種解法。

與上一篇算法的區別:

上一篇八皇后問題的解法是用三個一維數組的值表示該位置是否可以放置皇后,同樣,一維數組裏的值也是0和1,而位運算就是把一維數組的值拿出來,不看當前的整形值是多少,只看其二進制,這樣就等同於一維數組的效果,其思想都是根據行循環,然後看列和左右斜線看是否能放置皇后。





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章