Now, instead outputting board configurations, return the total number of distinct solutions.
Naive Way:在N-queens 中,要求是返回所有解法,那么就知道解法的个数了,但是作为一道新题,是否说明可以用低于返回所有解法的复杂度求得解的个数。
最后还是用了DFS原来的方法,做了一些简化。
public class Solution {
class Node{
int x,y;
Node(int xx, int yy){
x = xx;
y = yy;
}
}
public int totalNQueens(int n) {
// DFS
int board[][] = new int[n][n];
int count = 0;
Stack<Node> stack = new Stack<Node>();
for(int j = 0;j < n;j++)
stack.push(new Node(0,j));
while(!stack.isEmpty()){
Node node = stack.pop();
boolean hasNext = false;
setBoard(board,node,false);
for(int j = 0;j < n;j++)
if(node.x+1 < n && board[node.x+1][j] == 0) stack.push(new Node(node.x+1,j));
for(int j = 0;j < n;j++)
if(node.x+1 < n &&board[node.x+1][j] == 0) hasNext = true;
count += node.x==n-1?1:0;
if(!hasNext){
// back trace
if(stack.isEmpty()) break;
Node peek = stack.peek();
for(int i = node.x;i >= peek.x;i--)
for(int j = n-1;j >= 0;j--)
if(((i==node.x && j <= node.y) || (i==peek.x && j > peek.y) || (i< node.x && i > peek.x)) && board[i][j]==-1)
setBoard(board, new Node(i,j), true);
}
}
return count;
}
private void setBoard(int[][] b, Node node, boolean clear){
int add = clear?-1:1;
for(int i = 0;i < b.length;i++)
b[i][node.y] +=b[i][node.y]==-1?0:add;
for(int j = 0;j < b[0].length;j++)
b[node.x][j] +=b[node.x][j]==-1?0:add;
for(int i = 0;i < b.length;i++){
if(node.x+i < b.length && node.y+i < b[0].length)
b[node.x+i][node.y+i] += b[node.x+i][node.y+i]==-1?0:add;
if(node.x+i < b.length && node.y-i >= 0)
b[node.x+i][node.y-i] += b[node.x+i][node.y-i]==-1?0:add;
if(node.x-i >= 0 && node.y+i < b[0].length)
b[node.x-i][node.y+i] += b[node.x-i][node.y+i]==-1?0:add;
if(node.x-i >= 0 && node.y-i >= 0)
b[node.x-i][node.y-i] += b[node.x-i][node.y-i]==-1?0:add;
}
b[node.x][node.y] = clear?0:-1;
}
}
Improved Way:在Discuss看到很多人都不需要用二维矩阵。方法都特别厉害。
https://oj.leetcode.com/discuss/18411/accepted-java-solution
的做法是将所有格子分为col, row, diag1, diag2。col和row 是指列和行。
diag1是指从左上至右下的对角线, diag2是指从左下至右上的对角线。这样就将一个矩阵拆成4个1D向量。
最厉害的还是代码的回溯方式,遍历一个row之后可立即消去之前遍历的痕迹。
/**
* don't need to actually place the queen,
* instead, for each row, try to place without violation on
* col/ diagonal1/ diagnol2.
* trick: to detect whether 2 positions sit on the same diagnol:
* if delta(col, row) equals, same diagnol1;
* if sum(col, row) equals, same diagnal2.
*/
private final Set<Integer> occupiedCols = new HashSet<Integer>();
private final Set<Integer> occupiedDiag1s = new HashSet<Integer>();
private final Set<Integer> occupiedDiag2s = new HashSet<Integer>();
public int totalNQueens(int n) {
return totalNQueensHelper(0, 0, n);
}
private int totalNQueensHelper(int row, int count, int n) {
for (int col = 0; col < n; col++) {
if (occupiedCols.contains(col))
continue;
int diag1 = row - col;
if (occupiedDiag1s.contains(diag1))
continue;
int diag2 = row + col;
if (occupiedDiag2s.contains(diag2))
continue;
// we can now place a queen here
if (row == n-1)
count++;
else {
occupiedCols.add(col);
occupiedDiag1s.add(diag1);
occupiedDiag2s.add(diag2);
count = totalNQueensHelper(row+1, count, n);
// recover
occupiedCols.remove(col);
occupiedDiag1s.remove(diag1);
occupiedDiag2s.remove(diag2);
}
}
return count;
}
然后还有一些用bit manipulate的做法,我就看懂了一个,觉得这种做法最厉害之处在于不用back trace。来自leetcode用户
public class Solution {
int count=0;
public int totalNQueens(int n) {
bts(0,n,0,0,0);
return count;
}
//passing column mask, left diagonal mask, right diagonal mask
void bts(int row, int n, int cm, int dm1, int dm2){
if (row==n){
count++;
return;
}
for (int col=(1<<(n-1));col>0;col>>=1)
if ((col & (cm|dm1|dm2) )==0 )
bts(row+1,n, cm|col, (col|dm1)<<1, (col|dm2)>>1);
}
}
No comments:
Post a Comment