zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    线程中断总结
    线程的基本协作和生产者消费者
    synchronized总结
    线程基础总结
    Java集合总结(三):堆与优先级队列
    Java集合总结(二):Map和Set
    Java集合总结(一):列表和队列
    java枚举类型总结
    java 内部类简单总结
    java Integer包装类装箱的一个细节
  • 原文地址:https://www.cnblogs.com/yrbbest/p/4436362.html
Copyright © 2011-2022 走看看