N-Queens(N皇后问题)

题目:

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.."]
 ]


题解:

这道题很经典,网上有很多讲解实例。

在国际象棋中,皇后最强大,可以横竖斜的走。所以检查是否冲突就是不要同一行、一列和同一斜线上(行的差和列的差值的绝对值相等)

用一个循环递归处理子问题。这个问题中,在每一层递归函数中,我们用一个循环把一个皇后填入对应行的某一列中,如果当前棋盘合法,我们就递归处理先一行,找到正确的棋盘我们就存储到结果集里面。
这种题目都是使用这个套路,就是用一个循环去枚举当前所有情况,然后把元素加入,递归,再把元素移除,这道题目中不用移除的原因是我们用一个一维数组去存皇后在对应行的哪一列,因为一行只能有一个皇后,如果二维数组,那么就需要把那一行那一列在递归结束后设回没有皇后,所以道理是一样的。

这道题最后一个细节就是怎么实现检查当前棋盘合法性的问题,因为除了刚加进来的那个皇后,前面都是合法的,我们只需要检查当前行和前面行是否冲突即可。检查是否同列很简单,检查对角线就是行的差和列的差的绝对值不要相等就可以。


经典的DFS递归回溯解法,大体思路就是对每一行,按每一列挨个去试,试到了就保存结果没试到就回溯。
难点大概就是用1个一维数组存皇后所在的坐标值。对于一个棋盘来说,每个点都有横纵坐标,用横纵坐标可以表示一个点。
而这道题巧就巧在,每一行只能有一个皇后,也就是说,对于一行只能有一个纵坐标值,所以用1维数组能提前帮助解决皇后不能在同一行的问题。
那么用一维数组表示的话,方法是:一维数组的下标表示横坐标(哪一行),而数组的值表示纵坐标(哪一列)

 

见代码:

class Solution {
    public List<List<String>> solveNQueens(int n) {
        List<List<String>> res=new ArrayList<List<String>>();
        helper(n,0,new int[n],res);
        return res;
    }
    /*从第一行开始一次给每一行添加皇后。
   
    row:给当前行添加皇后
    colForRow:这个数组下标表示行,值表示该行对应的列的位置,这个行列表示皇后的位置。因为在该数组中,行下标只能对应一个值,所以一行只能一个元素,不用再判断两个皇后是不是在同一行了。
    因为用一维数组表示,就不用检查是否同行了,对于同列的判断,只要两个行下标对应的值相同,就是在一列。对于斜线,行的差和列的差对应的绝对值是否相等
    */
    public void helper(int n,int row,int[] colForRow,List<List<String>> res){
        if(n==row){//所有行遍历结束
            List<String> list=new ArrayList<>();
            for(int i=0;i<n;i++){
                StringBuilder sb=new StringBuilder();
                for(int j=0;j<n;j++){
                    if(colForRow[i]==j)
                        sb.append("Q");
                    else
                        sb.append(".");
                }
                list.add(sb.toString());
            }
            res.add(list);
            return ;
        }
        
        for(int j=0;j<n;j++){//遍历n列
            colForRow[row]=j;//先将皇后放在该行的j位置。
            if(check(row,colForRow)){//如果该位置可以放,就递归。如果不让放,进行下一轮循环,j会重新取值。
                helper(n,row+1,colForRow,res);
            }
            //回溯回来以后不用删除上一次的值,因为下一轮循环会覆盖该位置的值
        }
                
    }
    
    private boolean check(int row,int[] colForRow){
        //因为是从第一行开始添加的,所以row后面没有皇后。
        for(int i=0;i<row;i++){
            if(colForRow[i]==colForRow[row]||Math.abs(colForRow[i]-colForRow[row])==row-i)
                return false;
        }
        return true;
    }
}

 

posted on 2018-01-19 21:17  夜的第八章  阅读(186)  评论(0编辑  收藏  举报

导航