51. N-Queens

题目:

The n-queens puzzle is the problem of placing n queens on an n×n chessboard such that no two queens attack each other.

Given an integer n, return all distinct solutions to the n-queens puzzle.

Each solution contains a distinct board configuration of the n-queens' placement, where 'Q' and '.' both indicate a queen and an empty space respectively.

For example,
There exist two distinct solutions to the 4-queens puzzle:

[
 [".Q..",  // Solution 1
  "...Q",
  "Q...",
  "..Q."],

 ["..Q.",  // Solution 2
  "Q...",
  "...Q",
  ".Q.."]
]

 

Hide Tags
 Backtracking
Show Similar Problems
 

链接: http://leetcode.com/problems/n-queens/

题解:

又一道卡了很久的题。 N皇后问题,经典的NP-Complete。常见思路是dfs + backtracking。这里表面是暴力遍历所有的nn种可能,但backtracking的pruning可以减少决大部分的case。 因为皇后不可以处于同一行或列,所以每个结果肯定是n的一个排列,可以用一个数组queens来记录从行0到行n - 1中,不同queen的列坐标。用一个变量pos来控制行的增加以及进入下一层dfs。

Time Complexity - O(nn),  Space Complexity - O(n)   <- 递归栈的深度

public class Solution {
    List<List<String>> res = new ArrayList<>();
    
    public List<List<String>> solveNQueens(int n) {
        if(n <= 0)
            return res;
        int[] queens = new int[n];      // queens must be a permutation of n
        trySolveNQueens(queens, 0);
        return res;
    }
    
    private void trySolveNQueens(int[] queens, int pos) {
        int len = queens.length;
        if(pos == len)
            addSolution(queens);
        else {
            for(int i = 0; i < len; i++) {
                queens[pos] = i;
                if(isBoardValid(queens, pos))
                    trySolveNQueens(queens, pos + 1);
            }
        }
    }
    
    private boolean isBoardValid(int[] queens, int pos) {
        for(int i = 0; i < pos; i++) {
            if(queens[i] == queens[pos])        // column conflicts
                return false;
            else if(Math.abs(queens[pos] - queens[i]) == Math.abs(i - pos))  // diagonal conflicts
                return false;
        }
        
        return true;
    }
    
    private void addSolution(int[] queens) {
        ArrayList<String> list = new ArrayList<>();
        int len = queens.length;
        
        for(int i = 0; i < len; i++) {                  //loop through rows
            StringBuilder sb = new StringBuilder();
            
            for(int j = 0; j < len; j++) {          // loop through cols
                if(queens[i] == j)
                    sb.append('Q');
                else
                    sb.append('.');
            }
            
            list.add(sb.toString());
        }
        res.add(list);
    }
}

 

二刷:

Java:

二刷又卡了这道题,看了google的NQueens专题网站才知道有很多很好的解法。

这里我们用最普通的brute force。从头开始分析:

  1. 输入是int n,输出要求所有 n x n范围内的NQueens的合理解。我们把主要面对的问题分解,一步一步建立不同的子问题块
  2. 首先我们有题目给定的solveNQueens method。
    1. 在这个方法里我们主要定义一个结果集res,
    2. 判断基本的边界条件
    3. 建立一个int[n]数组queens。建立这个数组的目的是从数组的index来保存Queen的行坐标,queens[i]表示数组的列坐标,这样做的好处是我们可以不用check同一行内是否有重复的Queen
    4. 调用trySolveQueens(res, queens, 0)
    5. 返回结果
  3. 接下来我们需要一个dfs helper,这个method我定义叫trySolveNQueens
    1. 假如pos == queens.length,这时候说明之前的 0 ~ n - 1歌queen都放置完毕并且位置合理,这时候我们可以把结果加入结果集中
    2. 否则我们尝试在pos这一行的每一列放置Queen,假如放置完Queen以后board依然合理,则我们进入到下一层dfs,尝试在下一行的不同位置里放置Queen
  4. 一个辅助判断当前board是否valid的method isBoardValid
    1. 我们只用验证从0到当前的position pos之前的Queens
      1. 不需要验证行
      2. 验证是否有两个Queen存在同一列上 queens[i] == queens[pos]
      3. 验证对角线Math.abs(queens[i] - queens[pos]) == Math.abs(i - pos);
      4. 假如一切都合理,返回true
  5. 一个输出结果的method - collectSolution, 把记录下来的结果用题目要求的方式加入到结果集中

Time Complexity - O((n x n)n x n), Space Complexity - O(n2)

public class Solution {
    public List<List<String>> solveNQueens(int n) {
        List<List<String>> res = new ArrayList<>();
        if (n <= 0) {
            return res;
        }
        int[] queens = new int[n];
        trySolveNQueens(res, queens, 0);
        return res;
    }
    
    private void trySolveNQueens(List<List<String>> res, int[] queens, int pos) {
        if (pos == queens.length) {
            collectSolution(res, queens);
        } else {
            for (int i = 0; i < queens.length; i++) {
                queens[pos] = i;
                if (isBoardValid(queens, pos)) {
                    trySolveNQueens(res, queens, pos + 1);
                }
            }
        }
    }
    
    private boolean isBoardValid(int[] queens, int pos) {
        for (int i = 0; i < pos; i++) {
            if (queens[i] == queens[pos]) {
                return false;
            }
            if (Math.abs(queens[i] - queens[pos]) == Math.abs(i - pos)) {
                return false;
            }
        }
        return true;
    }
    
    private void collectSolution(List<List<String>> res, int[] queens) {
        List<String> sol = new ArrayList<>();
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < queens.length; i++) {
            sb.setLength(0);
            for (int j = 0; j < queens.length; j++) {
                if (j != queens[i]) {
                    sb.append('.');
                } else {
                    sb.append('Q');
                }
            }
            sol.add(sb.toString());
        }
        res.add(sol);
    }
}


另外一种比较慢的方法,传递整个n x n数组,判断下一步是否可以走。

Time Complexity - O((n x n)n x n), Space Complexity - O(n2)

public class Solution {
    public List<List<String>> solveNQueens(int n) {
        List<List<String>> res = new ArrayList<>();
        if (n <= 0) {
            return res;
        }
        for (int i = 0; i < n; i++) {
            canSolveNQueens(res, new int[n][n], 0, i);
        }
        return res;
    }
    
    private boolean canSolveNQueens(List<List<String>> res, int[][] board, int row, int col) {
        if (row == board.length - 1) {
            board[row][col] = 1;
            addSolution(res, board);
            board[row][col] = 1;
            return false;
        }
        board[row][col] = 1;
        for (int i = 0; i < board.length; i++) {
            if (isBoardValid(board, row + 1, i)) {
                if (!canSolveNQueens(res, board, row + 1, i)) {
                    board[row + 1][i] = 0;
                }
            }
        }
        return false;
    }
    
    private boolean isBoardValid(int[][] board, int row, int col) {
        int n = board.length;
        if (!isValidRange(board, row, col)) {
            return false;
        }
        for (int i = 0; i < board.length; i++) {
            if (board[i][col] == 1 || board[row][i] == 1) {
                return false;
            }
        }
        for (int i = 0; i < n; i++) {
            if (isValidRange(board, row - i, col + i) && board[row - i][col + i] == 1)
                return false;
            if (isValidRange(board, row - i, col - i) && board[row - i][col - i] == 1)
                return false;
        }
        return true;
    }
    
    private boolean isValidRange(int[][] board, int x, int y) {
        return x >= 0 && x < board.length && y >= 0 && y < board.length;
    }
    
    private void addSolution(List<List<String>> res, int[][] board) {
        List<String> solution = new ArrayList<>();
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < board.length; i++) {
            sb.setLength(0);
            for (int j = 0; j < board[0].length; j++) {
                if (board[i][j] == 1) {
                    sb.append('Q');
                } else {
                    sb.append('.');
                }
            }
            solution.add(sb.toString());
        }
        res.add(solution);
    }
}

 

Reference:

http://www.algorist.com/

https://developers.google.com/optimization/puzzles/queens?hl=en#python

http://introcs.cs.princeton.edu/java/home/

http://www.geeksforgeeks.org/backtracking-set-3-n-queen-problem/

http://code.geeksforgeeks.org/po2i2q

https://leetcode.com/discuss/24717/comparably-concise-java-code

https://leetcode.com/discuss/63710/ac-python-76-ms-iterative-backtracking-solution

https://leetcode.com/discuss/49179/fast-short-and-easy-understand-python-solution-11-lines-76ms

https://leetcode.com/discuss/35128/accepted-solution-use-backtracking-bitmask-easy-understand

https://sites.google.com/site/nqueensolver/home/algorithm-results

posted @ 2015-04-17 23:37  YRB  阅读(722)  评论(0编辑  收藏  举报