LeetCode 筆記系列 14 N-Queen II [思考的深度問題]


題目: Follow up for N-Queens problem.

  Now, instead outputting board configurations, return the total number of distinct solutions.

就是讓你輸出N皇后問題的解法數目。

直接求解,每次還記錄整個棋盤位置那種方法就不說了,必須超時。

有一個牛逼大了的超級無敵帥的位移動解法。我們暫且不表。看看我當時想的一個解法。

首先,對於皇后的那個遞歸方法,我有三個變量分別表示1.一個int值,表示遞歸到當前的level,當前的哪幾個col被占領了,例如11011就表示我們下一個Q只能放在第三個(從第一個位置開始數)位置上了;2.一個hash table, 表示左對角線到目前為止,是否有Queen占領過了;2.一個hash table,表示右對角線是否有Queen占領過了。然后每次都去查詢row上的可用格子,再去看看兩個hash table上的可用格子,一起決定這一層我們選取那一(幾)個作為備選項。

說說那個表示對角線的hash table,是一個Integer到boolean的映射。因為,所有某條固定左對角線上的點(row, col),row - col都是定值,那么可以用row - col表示一條左對角線嘛!同理可以用row + col表示右對角線嘛。

然后任意一個點,我們就知道它左右對角線的key了,就可以去那個hash 表里面查看是不是有Queen占領過啦。

所以,給出一個當前被占領的狀態,一個左對角線被占領的狀態,一個右對角線被占領的狀態,我們就可以計算出下一步在哪里放Queen啦。方法如下:

1  private static int available(int cur_row_status, 
2             HashMap<Integer, Boolean> left_digra_status, HashMap<Integer, Boolean> right_digra_status, int row, int n){
3         int avail = cur_row_status;
4         for(int j = 0; j < n; j++){
5             if((left_digra_status.containsKey(row + j) && left_digra_status.get(row + j))
6                     || (right_digra_status.containsKey(row - j) && right_digra_status.get(row - j))) avail = set(avail, j);
7         }
8         return avail;
9     }
available方法

例如,返回如果是而110011(二進制),那么中間兩位是可以放Q的。

那,我們就可以定義遞歸函數咯哦。

private static void collectSolutions(int cur_row_status, 
            HashMap<Integer, Boolean> left_digra_status, HashMap<Integer, Boolean> right_digra_status, 
            int row, int n, int[] count){
        if(row == n) {
            count[0]++;
            return;
        }
        int avail = available(cur_row_status, left_digra_status,right_digra_status, row, n);
        for(int i = 0; i < n; i++){
            if(!isSet(avail, i)){
                left_digra_status.put(row + i, true);
                right_digra_status.put(row - i, true);
                collectSolutions(set(cur_row_status, i), left_digra_status, right_digra_status, row + 1, 
                        n, count);
                left_digra_status.put(row + i, false);
                right_digra_status.put(row - i, false);
            }
        }
    }
collectSolutions

count里面就放我們的計數。注意每次遞歸子函數返回后,要重新設置對角線。col的那個狀態不用重置了,因為java函數不會改變int值的。

主函數這樣寫:

 1 public static int  totalNQueens(int n) {
 2         // Start typing your Java solution below
 3         // DO NOT write main() function
 4         int cur_row_status = 0;
 5         int[] count = new int[1];
 6         HashMap<Integer, Boolean> left_digra_status = new HashMap<Integer, Boolean>();
 7         HashMap<Integer, Boolean> right_digra_status = new HashMap<Integer, Boolean>();
 8         collectSolutions(cur_row_status, left_digra_status, right_digra_status, 0, n, count);
 9         return count[0];
10     }

然后我覺得我這方法已經不錯了吧,結果還是讓大集合潮濕了。

牛逼閃閃,刺瞎我的24k鈦合金狗眼的位移算法來鳥。

 1 public int totalNQueens(int n){
 2         cnt = 0;
 3         upper = (1<<n)-1 ;
 4         Queen(0,0,0);
 5         return cnt;
 6     }
 7     
 8     //為啥說大牛niub呢,看看我下面那個,再對比ld和rd,人大牛一眼就看出來了,沒必要保存
 9     //所有對角線信息啊。下一個狀態,完全由當前狀態決定!!
10     private void Queen(int row, int ld, int rd){//ld, left 對角線; rd, right 對角線
11          int pos, p;
12          if(row!=upper)
13          {
14              //so pos in binary is like, under current row/ld/rd restriction, what is available slot to put Q
15              pos = upper & (~(row | ld |rd));
16              while(pos!=0)//available is 1
17              {
18                  p = pos & (-pos);//from right to left, the first "1" in pos
19                  //now, we occupy the most right available position
20                  pos = pos - p;//now take this available as ”Q“,pos kind of like a available slot marker
21                  Queen(row+p,(ld+p)<<1,(rd+p)>>1);
22              }
23          }
24          else ++cnt;
25     }
Niubility N Queen

好一個不明覺厲,男默女淚的算法!

。。。。。

首先,再次承認和牛人的差距。

其次,反思,反思,深刻地反思。為毛牛人就知道這一點呢?其實所有中間信息都可以用一個整數來表示啊。

特別是那個左右對角線的事情,弄的本娃很郁悶。仔細想想,可不是嘛,當設置了一個Q以后,就是設置其左下方和右下方不能訪問嘛。隨着層次的深入(向最后一行靠近),對角線的狀態可不就是左對角線左移,右對角線右移嘛。天,好有畫面感的事情。

這里還有個小技巧。11100這個二進制數,怎么知道從右向左邊第一個1的位置啊?

p = (pos) & (-pos)

我真是不知道這個,如果你也不知道負數在計算機中的表示方法的話,建議google之。

哦,這里有個關於這個算法的圖圖,看看有幫助。http://www.matrix67.com/blog/archives/266

我想去買這位blogger的書了。

總結:

1. 思考要有深度。就是說,理解一下,當前的信息到底是怎么樣得出來的,而不是看表象。

2. 要有畫面感

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM