• leetcode| 51. N皇后问题


    ##n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。 ![](https://img2018.cnblogs.com/blog/1805130/202002/1805130-20200212172806396-73368217.png) 上图为 8 皇后问题的一种解法。

    给定一个整数 n,返回 n 皇后不同的解。

    示例:
    输入: 4
    解释: 4 皇后问题存在如下两个不同的解法。
    [
     [".Q..",  // 解法 1
      "...Q",
      "Q...",
      "..Q."],

    ["..Q.",  // 解法 2
      "Q...",
      "...Q",
      ".Q.."]
    ]

    n皇后问题有两种解的要求,一种仅要求输出解决方案个数,一种要求输出所有的具体解决方案。

    思路

    暴力破解不可行,时间复杂度为O(n^n)。
    回溯法,其中在进行深搜时,剪枝的关键在于:

    • 对于任意一点(x,y),其所在的主对角线()和副对角线(/)上的坐标点,分别满足一定关系。
    • n*n的矩阵,型对角线和/型对角线分别有2n-1条。
    • 任意一条型对角线上的坐标点满足 row-col=常量a ,且a的范围为-(n-1)到n-1。
    • 任意一条/型对角线上的坐标点满足 row+col=常量b ,且b的范围为0到2n-2。
    • 如4*4矩阵
    • 使用一维数组x,y,z可以分别标记、/和纵方向是否已有棋子(按行放置棋子,无需再考虑横向)。

    每一行放置时,相比于上一行能放置棋子的空位就会少1,时间复杂度O(n!),空间复杂度O(n)。

    仅求解决方案个数代码

    class Solution {
        public int dfs_backtrace(int row, int count, int n, int[] z, int[] x, int[] y) {
            //每一层递归携带了row参数,所以每个dfs_backtrace函数代表row行在尝试放置棋子
            //因此只需一层for循环遍历row行的每一列
            for(int col = 0; col < n; col++){
                //如果各个方向都没有标记,说明此处可以放置棋子
                if(z[col] + x[n + row - col] + y[row + col] == 0) {
                    //标记
                    z[col] = 1;
                    x[n + row - col] = 1;
                    y[row + col] = 1;
                    //dfs
                    if(row + 1 == n) {
                        count++;
                    } else {
                        count = dfs_backtrace(row+1, count, n, z, x, y);
                    }
                    //回溯
                    z[col] = 0;
                    x[n + row -col] = 0;
                    y[row + col] = 0;
                }
            }
            return count;
        }
    
    	public int totalNQueens(int n) {
            int[] z = new int[n];    // z[i]表示第i列是否有棋子
            int[] x = new int[2*n];  // x[n + row - col]表示(row,col)点所在主对角线()是否有棋子
            int[] y = new int[2*n];  // y[row + col]表示(row,col)点所在副对角线(/)是否有棋子
    		return dfs_backtrace(0, 0, n, z, x, y);
    	}
    }
    

    求出所有解决方案的代码

    仅需要添加一个queue[n]数组,queue[i] = j, 表示为第i行的皇后放置在第j列;
    将queue在放棋子与撤销棋子时同步更新即可,引入queue数组是为了记录皇后位置,用于输出解决方案。
    以下代码与上个代码思路完全一致,仅将一些过程写成了函数。

    class Solution {
        int n;
        int[] x;
        int[] y;
        int[] z;
        int[] queue;
    
        List<List<String>> output = new ArrayList<List<String>>();
    
        private boolean is_not_attack(int row, int col) {
            return x[n+row-col]+y[row+col]+z[col] == 0;
        }
    
        private void placeQueue(int row, int col) {
            x[n + row - col] = 1;
            y[row + col] = 1;
            z[col] = 1;
            queue[row] = col;
        }
    
        private void removeQueue(int row, int col) {
            x[n + row - col] = 0;
            y[row + col] = 0;
            z[col] = 0;
            queue[row] = 0;
        }
    
        private void addOutput() {
            List<String> list = new ArrayList<String>();
            for(int i = 0; i < n; i++) {
                char[] str = new char[n];
                Arrays.fill(str, '.');
                str[queue[i]] = 'Q';
                list.add(new String(str));
            }
            output.add(list);
        }
    
        private void traceback(int row) {
            for(int col = 0; col < n; col++) {
                if(is_not_attack(row, col)) {
                    //放置棋子
                    placeQueue(row, col);
                    //DFS
                    if(row + 1 == n) {
                        addOutput();
                    } else {
                        traceback(row + 1);
                    }
                    //回溯棋子
                    removeQueue(row, col);
                }
            }
        }
    
        public List<List<String>> solveNQueens(int n) {
            this.n = n;
            x = new int[2*n];
            y = new int[2*n];
            z = new int[n];
            queue = new int[n];
            traceback(0);
            return output;
        }
    }
    

    笔记

    方阵主副对角线行列值的特殊关系。

    递归:算法结构,函数调用自身。
    回溯:算法思想,会“剪枝”的穷举。
    DFS:回溯搜索是深度优先搜索的一种,回溯法在搜索过程中不保留完整树结构,DFS搜索树结构完整。

    链接:https://leetcode-cn.com/problems/n-queens
    链接:https://leetcode-cn.com/problems/n-queens-ii

  • 相关阅读:
    动手动脑3
    AWK编程与应用
    BASH内置变量的使用
    服务器交互脚本expect
    编程对话框的界面程序
    每日打卡
    AppiumLibrary中文翻译
    Bootstrap4简单使用
    Python基础06-类与对象
    BDD模式-Python behave的简单使用
  • 原文地址:https://www.cnblogs.com/ustca/p/12300045.html
Copyright © 2020-2023  润新知