Leecode51、52 N皇后

题目:

n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。

在这里插入图片描述
上图为 8 皇后问题的一种解法。(皇后攻击范围无限远,但只能攻击四个方向,水平、垂直
以及两条对角线方向)

给定一个整数 n,返回所有不同的 n 皇后问题的解决方案。

每一种解法包含一个明确的 n 皇后问题的棋子放置方案,该方案中 ‘Q’ 和 ‘.’ 分别代表了皇后和空位。

示例:

输入: 4
输出: [
[".Q…", // 解法 1
“…Q”,
“Q…”,
“…Q.”],

["…Q.", // 解法 2
“Q…”,
“…Q”,
“.Q…”]
]
解释: 4 皇后问题存在两个不同的解法。

思路:

  1. 这是一个典型的用回溯解决的问题,也可以认为是深搜,个人理解深搜一般都伴随着回溯,可以认为每一行就是一层深度,最终目的是在每一行都放下一枚皇后,而他们不能互相攻击,简单的枚举所有摆放情况效率太低,通过分析已经摆放过的皇后,可以得出剩下的层中,哪些位置是不能摆放的,从而剪掉一些分支,提升效率;规则为:
    1. 每行放一个;
    2. 已经放了皇后的列,其他行在这一列不能再放皇后;
    3. 与已经放了皇后的左上到右下,右上到左下的斜边相交的其他行也不能放;

代码分析:

  1. 第一行随便放哪里都行,所以遍历每个位置,每次都进行深搜,搜到最后一层,如果满足要求就将结果存入结果集中;
  2. 每放一个皇后,我们都对棋盘上受到影响的位置进行标记,在下一曾摆放皇后的时候就避开这些位置,以提高效率,列的影响标记很好解决,n列只需要用n个长度的boolean数组即可标志哪些列是不能放的;
  3. 对角的标志则比较麻烦一些,如果把行看作y坐标,把列看作x坐标,则,左上到右下的格子的左边满足 x+y = c(常数),右上到左下 x-y = c(常数),那么我们只需要用x+y 或者 x - y来标记哪些斜线上已经放置了皇后,两个方向的斜线分别有2n-1条,我们使用2n长度的boolean数组:xySum及xyDif 即可记录;
  4. 注意回溯时恢复状态;

代码实现:

class Solution {
    public List<List<String>> solveNQueens(int n) {
        List<List<String>> result = new LinkedList<>();
        List<String> list = new LinkedList<>();
        DFS(result,list,0,new boolean[n],new boolean[2*n],new boolean[2*n],n);
        return result;
    }
    /*
    * cols 已经被占据的列,这些列不能再放置皇后
    * xySum:已被占据的左上到右下的斜线
    * xyDif:已被占据的右上到左下的斜线
    */
    public void DFS(List<List<String>> result, List<String> list, int level,boolean[] cols,boolean[] xySum ,boolean[] xyDif,int n){
        if(level >= n){
            LinkedList<String> temp = new LinkedList<String>();
            temp.addAll(list);
            result.add(temp);
        }
        for(int i = 0;i < n;++i){
            if(isValid(level,i,cols,xySum,xyDif,n)){
                char[] buffer =new char[n];
                Arrays.fill(buffer,'.');
                buffer[i] = 'Q';
                list.add(new String(buffer));
                //标记已被占据的空间
                setInvalid(level,i,cols,xySum,xyDif,n);
                DFS(result,list,level+1,cols,xySum,xyDif,n);
                //回溯,撤销影响
                setValid(level,i,cols,xySum,xyDif,n);
                list.remove(list.size()-1);
            }
        }
    }
    private void setInvalid(int level,int col,boolean[] cols,boolean[] xySum,boolean[] xyDif,int n){
        cols[col] = true;
        xySum[level+col] = true;
        xyDif[level-col + n - 1] = true;
    }
    private void setValid(int level,int col,boolean[] cols,boolean[] xySum,boolean[] xyDif,int n){
        cols[col] = false;
        xySum[level+col] = false;
        xyDif[level-col + n - 1] = false;
    }
    private boolean isValid(int level,int col, boolean[] cols,boolean[] xySum,boolean[] xyDif,int n){
        if (cols[col]) return false;
        if (xySum[level + col]) return false;
        if (xyDif[level - col + n - 1]) return false;
        return true;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值