51. N 皇后(回溯算法)

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

给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。
每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 ‘Q’ 和 ‘.’ 分别代表了皇后和空位。

示例 1:
在这里插入图片描述
输入:n = 4
输出:[[".Q…","…Q",“Q…”,"…Q."],["…Q.",“Q…”,"…Q",".Q…"]]
解释:如上图所示,4 皇后问题存在两个不同的解法。

示例 2:
输入:n = 1
输出:[[“Q”]]

提示:

1 <= n <= 9
皇后彼此不能相互攻击,也就是说:任何两个皇后都不能处于同一条横行、纵行或斜线上。

N皇后问题是典型的回溯问题, 解决回溯问题最好的方法就是先能画出树状图,这道题的画法是这这样的如图:
在这里插入图片描述
每一列通过for循环进行遍历,每一行通过递归进行遍历,这里画不下了,知道怎么画就行了;

那么这道题只要找到最后的叶子节点,就是答案之一了;

递归结束条件通过画图也很容易发现,只要递归的层数row等于n时,就到了叶子节点了;

然后就可以通过for循环确定每一列column,递归确定每一行row,这样通过行列是否满足棋盘规则就可以确定每个位置能不能放皇后Q了;

这个棋盘规则也很简单,就是三点
1,不能同行
2,不能同列
3,不能同斜线 (45度和135度角)

这里面我们其实并不需要对行进行检查,因为每一次的递归,行数都会相应增加,所以在一次次递归中,行的检查就已经通过列的检查覆盖完了;

其他就没有什么难度了,这道题只要能想到这个图怎么画的,知道行和列的关系就没有问题了;
详细注释在代码里,可以比较着看
代码如下:

class Solution {
private: 
    vector<vector<string>> ans;
    //n是棋盘大小,row是递归到的行数
    void backTracking(int n, int row, vector<string>& chessboard) {
        //当行数为棋盘的最后一行时,递归结束
        if (row == n) {
            ans.push_back(chessboard);
            return ;
        }
        //for循环是对每一列column进行的遍历,就是类似于宽度,
        //递归则是对每一行row进行的遍历,及深度的遍历
        for (int column = 0; column < n; ++column) {
            //如果该行row该列column这个位置合法,就可以放皇后Q
            if (isValid(row, column, chessboard, n)) {
                chessboard[row][column] = 'Q';
                backTracking(n, row + 1, chessboard);//递归
                chessboard[row][column] = '.';//回溯
            }
        }
    }
    //判断是否合法
    bool isValid(int row, int column, vector<string>& chessboard, int n) {
        //检查第column列有没有Q出现
        for (int i = 0; i < row; ++i) {
            if(chessboard[i][column] == 'Q')
            return false;
        }
        //检查左斜线有没有Q(45度)
        for (int i = row - 1, j = column - 1; i >= 0 && j >=0; --i, --j) {
            if (chessboard[i][j] == 'Q') 
            return false;
        }
        //检查右斜线有没有Q(135度)
        for (int i = row - 1, j = column + 1; i >= 0 && j < n; --i, ++j) {
            if (chessboard[i][j] == 'Q')
            return false;
        }
        return true;
    }
public:
    vector<vector<string>> solveNQueens(int n) {
        vector<string> chessboard(n, string(n, '.'));
        backTracking(n, 0, chessboard);
        return ans;
    }
};

这种问题就属于棋盘类的问题,有一道相似的题目可以尝试一下:37. 解数独
这个题目其实是在原集合里进行的修改,所以不需要定义额外的变量,可以尝试一下;
带有详细注释的代码贴上:

class Solution {
private:
    bool backTracking(vector<vector<char>>& board) {
        //分别对行和列进行遍历,递归则是对这9个数进行的遍历
        for (int i = 0; i < board.size(); ++i) {
            for (int j = 0; j < board[0].size(); ++j) {
                //如果这个位置有数,就跳过
                if (board[i][j] != '.') continue;
                //对9个数进行遍历
                for (char ch = '1'; ch <= '9'; ++ch) {
                    //当前的数符合要求的话,就放到当前位置
                    if (isVaild(i, j, ch, board)) {
                        board[i][j] = ch;
                        //当递归到底找到了合适的一组后返回
                        if (backTracking(board)) return true;
                        board[i][j] = '.';//回溯
                    }
                }
                return false;//试完了9个数,都不满足,则放回false
            }
        }
        return true;//若结束了遍历没有返回false,则说明找到了合适的答案
    }
    bool isVaild(int row, int column, char ch, vector<vector<char>>& board) {
        //判断列是否有重复
        for (int i = 0; i < 9; ++i) {
            if (board[i][column] == ch)
            return false;
        }
        //判断行是否有重复
        for (int i = 0; i < 9; ++i) {
            if (board[row][i] == ch) 
            return false;
        }
        //针对每一个小九宫格,判断里面是否出现重复的元素
        int startRow = (row / 3) * 3;
        int startColumn = (column / 3) * 3;
        for (int i = startRow; i < startRow + 3; ++i) {
            for (int j = startColumn; j < startColumn + 3; ++j) {
                if (board[i][j] == ch)
                return false;
            }
        }
        return true;
    }
public:
    void solveSudoku(vector<vector<char>>& board) {
        backTracking(board);
    }
};

在力扣上一看还有一个N皇后||,和N皇后可以说一模一样,小改动一下就可以了,感觉比N皇后简单;
代码如下:

class Solution {
private: 
    int ans = 0;
    void backTracking(int n, int row, vector<string>& chessboard) {
        if (row == n) {
            ans++;
            return ;
        }
        for (int column = 0; column < n; ++column) {
            if (isVaild(n, row, column, chessboard)) {
                chessboard[row][column] = 'Q';
                backTracking(n, row + 1, chessboard);
                chessboard[row][column] = '.';
            }
        }
    }
    bool isVaild(int n, int row, int column, vector<string>& chessboard) {
        for (int i = 0; i < row; ++i) {
            if (chessboard[i][column] == 'Q')
            return false;
        }
        for (int i = row - 1, j = column - 1; i >= 0 && j >= 0; --i, --j) {
            if (chessboard[i][j] == 'Q')
            return false;
        }
        for (int i = row - 1, j = column + 1; i >= 0 && j < n; --i, ++j) {
            if (chessboard[i][j] == 'Q')
            return false;
        }
        return true;
    }
public:
    int totalNQueens(int n) {
        vector<string> chessboard(n, string(n, '.'));
        backTracking(n, 0, chessboard);
        return ans;
    }
};

注意:下面啥也不是

评论区看到了一个面向测试编程🙈,击败百分百
代码如下(java)

class Solution {
    public int totalNQueens(int n) {
        int[] rs = new int[]{0,1,0,0,2,10,4,40,92,352,724,2680};
        return rs[n];
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

YXXYX

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值