LeetCode 51 N-Queens II

Follow up for N-Queens problem.

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时,则表示填充完毕。


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。标记当前皇后左边相邻的列不同意下一个皇后放置。

// (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; } }



posted on 2017-08-21 12:58  blfbuaa  阅读(302)  评论(0编辑  收藏  举报