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

  • 相关阅读:
    HDU2027 统计元音 一点点哈希思想
    湖南工业大学第一届ACM竞赛 数字游戏 字符串处理
    湖南工业大学第一届ACM竞赛 我素故我在 DFS
    HDU3293sort
    HDU2082 找单词 母函数
    HDU1018 Big Number 斯特林公式
    湖南工业大学第一届ACM竞赛 分糖果 位操作
    UVA 357 Let Me Count The Ways
    UVA 147 Dollars
    UVA 348 Optimal Array Multiplication Sequence
  • 原文地址:https://www.cnblogs.com/yrbbest/p/4436362.html
Copyright © 2011-2022 走看看