八皇后位運算解 N-Queen II Leetcode
阿新 • • 發佈:2019-01-01
思路其實還是每使用一位就把橫著,和左斜方,右斜方的位站上,但是用位運算就有不一樣神韻。
先看程式碼:
int num; int upper; public int solve(int n){ upper=(1<<n)-1; dp(0,0,0); return num; } public void dp(int row, int ld,int rd){ System.out.println("dp: "+Integer.toBinaryString(row)+"\t"+Integer.toBinaryString(ld)+"\t"+Integer.toBinaryString(rd)); int pos,p; if(row!=upper){ pos=upper&(~(row|ld|rd)); // System.out.println("pos= "+Integer.toBinaryString(pos)); while(pos!=0){ p=pos&(-pos); pos=pos-p; dp(row+p,(ld+p)<<1,(rd+p)>>1); } }else num++; }
主要精妙在這幾個地方:容易看出來這是個dp,思路和普通解基本相同。
精妙1. 用row ld rd表示了橫豎斜三個方向上的資料,用1表示佔用,0表示可以。
精妙2. 用pos=upper&(~(row|ld|rd))來表示當前可行位置。 這個等會細說。
精妙3. 用p=pos&(-pos)表示第一個非0位。這個涉及到了負數在計算機裡的表示。
byte在計算機裡是8位,integer是32位,但是Integer.max_value 是(2<<31)-1 為什麼?
因為剩下的第32位上是1的數都用來表示負數了,對於任何正整數v,他的負數表示為
~(v-1)
所以6&(-6)=2;
pos裡面是用1表示可用,0表示不可用。
精妙4, 就是那個呼叫dp了,這裡連上精妙2一起說一下。
其實我們很容易想到用橫豎斜三種方式來表示三個緯度,第四個緯度自動步進1。問題就在於表述形式。在這裡row ld rd都是用位來表示,在當前迴圈中,ld,rd,row都表示該列上被佔用的位。 但這是怎麼實現的呢?
1. row很容易理解,p為幾,第幾行就被佔了,這沒的說。就是ld,rd
2. 注意到列是步進的,也就是說,你在這一列,p佔了第3行,下一列,一定被佔在第4行上,和在第2行上。到了下下列,一定佔在第1行和第5行上。這就是ld,和rd了。
(ld+p)<<1 不就是把所有的位左移1位擋住第4位麼?
(rd+p)>>1 擋住了不就是第二位麼?
第二次計算完了,到了下下列,再移動,擋住的不就是1行和5行了麼?
這就是神