LeetCode 51 N-Queens II

Follow up for N-Queens problem.java

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


思路1:打表
public class Solution {
	public int totalNQueens(int n) {
		int[] result = new int[] { 0, 1, 0, 0, 2, 10, 4, 40, 92, 352, 724,
				2680, 14200, 73712, 365596, 2279184, 14772512, 95815104,
				666090624 };
		return result[n];
	}
}
思路2:使用回溯法,參照 http://blog.csdn.net/mlweixiao/article/details/40984541
public class Solution {
	public static int num=0;
	private boolean isValid(List<Integer> al) {
		int column = al.get(al.size() - 1);
		for (int i = 0; i < al.size() - 1; i++) {
			if (column == al.get(i)
					|| Math.abs(column - al.get(i)) == Math.abs(al.size()-1 - i)) {
				return false;
			}
		}
		return true;
	}

	private void search(int n, List<Integer> al, int col) {
		if (col > n){
			num++;
		}else {
			for (int j = 0; j < n; j++) {
				al.add(j);
				if (isValid(al)) {
					search(n, al, col + 1);
				}
				al.remove(al.size()-1);
			}
		}
	}
	public int totalNQueens(int n) {
		num=0;
		search(n,new  LinkedList<Integer>(),1);
		return num;
	}
}
思路3 :回溯法。僅僅只是使用位移,速度超快,參考 http://blog.csdn.net/kai_wei_zhang/article/details/8033194
若是棋盤的前i-1行已經被填充。現在咱們需要填充第i行,那怎麼得出第i行哪些是可以填充呢?那麼有三種狀況是禁止,同一列,斜率k爲1的斜線,斜率k爲-1的斜線。要是咱們能用三個變量row。ld。rd來分別存儲這三種狀況的禁止位該多好啊。

如下是是六皇后問題的,若是前三行已經填充了。咱們需要填充第四行函數


當中row。ld,rd的二進制表示如上,1表明禁止位,0表明空位(可以放皇后)。

對於第四行而言。那麼第1,3。5列(從左往右數)不能放,故row的二進制表示爲101010;對於第四行而言。斜率k爲1的斜線有兩條對其有影響,最左上的一條對第四行沒有影響,k=1的中間一條斜線對第四行的影響是第一個位置(從左往右數)不能放,k=1的右下一條斜線對第四行的影響是第4個位置(從左往右數)不能放。故k=1的斜線對第四行的影響爲100100(從左往右數);而k=-1的三條斜線對第四行的影響各自是第4,5,6個位置(從左往右數)不能放,故rd的二進制表示爲000111(從左往右數),那麼這三個數row。rd。ld二進制取‘或’就可以獲得101111。101111對於第四行而言就意味着僅僅有第二個位置能放。佈局

那麼第四行填充了,這三個數row,rd。ld該怎麼變化呢?row與剛填充的第二個位置(用010000表示)取或就能夠。rd與剛填充的第二個位置(用010000表示)取或。還要向右移1位,因爲其斜率爲-1,第四行的禁止位相對於第五行而言是左上。同理,ld與剛填充的第二個位置(用010000表示)取或。還要向左移1位。當row的二進制的所有位全爲1時,則表示填充完畢。post


public class Solution {
    // sum用來記錄皇后放置成功的不一樣佈局數。upperlim用來標記所有列都已經放置好了皇后。

static int counter = 0; static long upperlim = 1; // 試探算法從最右邊的列開始,函數帶三個參數row、ld和rd。分別表示在縱列和兩個對角線方向的限制條件下這一行的哪些地方不能放 void search(long row, long ld, long rd) { if (row != upperlim) { // row,ld。rd進行「或」運算,求得所有可以放置皇后的列,相應位爲0。 // 而後再取反後「與」上全1的數。來求得當前所有可以放置皇后的位置。相應列改成1 // 也就是求取當前哪些列可以放置皇后 long pos = upperlim & ~(row | ld | rd); while (pos != 0) { // 0 -- 皇后沒有地方可放,回溯 // 拷貝pos最右邊爲1的bit。其他bit置0 // 也就是取得可以放皇后的最右邊的列 long p = pos & -pos;//至關於p = pos & (~pos + 1) // 將pos最右邊爲1的bit清零 // 也就是爲獲取下一次的最右可用列使用作準備, // 程序未來會回溯到這個位置繼續試探 pos -= p; // row + p,將當前列置1,表示記錄此次皇后放置的列。 // (ld + p) << 1。標記當前皇后左邊相鄰的列不一樣意下一個皇后放置。spa

// (ld + p) >> 1,標記當前皇后右邊相鄰的列不一樣意下一個皇后放置。 // 此處的移位操做其實是記錄對角線上的限制。僅僅是因爲問題都化歸 // 到一行網格上來解決。因此表示爲列的限制就可以了。顯然。隨着移位 // 在每次選擇列以前進行,原來N×N網格中某個已放置的皇后針對其對角線 // 上產生的限制都被記錄下來了 search(row + p, (ld + p) << 1, (rd + p) >> 1); } } else { // row的所有位都爲1,即找到了一個成功的佈局,回溯 counter++; } } public int totalNQueens(int n) { counter = 0; upperlim = (1 << n) - 1; test(0, 0, 0); return counter; } }.net

相關文章
相關標籤/搜索