八(n)皇后问题

959 阅读3分钟

1、八皇后问题

在8×8格的国际象棋上摆放8个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。

八皇后问题

​ ​ ​ ​ ​ ​ ​ 穷举法:将所有皇后的所有可能位置都摆放好,然后对每一种摆放位置进行判断,类似于全排列,时间复杂度是O(1),但如果是n皇后,时间复杂度就是O(nⁿ),这个复杂度是不能接受的。

​ ​ ​ ​ ​ ​ ​ 经典的回溯算法问题:选择第一个皇后摆放好,判断是否可以互相攻击到,如果不能则摆放下一个皇后,再次判断,如果可以互相攻击则进行回溯,把这个皇后摆放到下一个位置,····,八个皇后摆放完成,解法+1,回溯,寻找下一种解法。

​ ​ ​ ​ ​ ​ ​ 八皇后问题简单来说就两个动作:摆放棋子、判断是否会被攻击 ? (当前皇后还有位置可摆 ? 继续循环 : 回溯) : 摆放下一个棋子 ;

​ ​ ​ ​ ​ ​ ​ 并且每个皇后都是独立的,不同的个体。

代码如下:

/**
 * 这里我们会使用一维数组代替二维数组
 * 皇后摆放位置要求不能在同一行,我们使用一维数组的下标代表皇后所在列即可,这样皇后就不在同一列了
 * @author lxh
 * @date 2020-09-03 14:35
 */
public class EightQueenProblem {
    //使用一个长度为8的数组解决,下标即为皇后所在列, 数组元素即为皇后所在行
    int[] queen = new int[ 8 ];
    int count = 0;

    public static void main(String[] args) {
        EightQueenProblem eightQueenProblem = new EightQueenProblem();
        // 0 代表 第一个皇后 ··· 7代表第8个皇后
        eightQueenProblem.placeQueen(0);
        // 八皇后问题有 92 种解法
        System.out.println(eightQueenProblem.count);
    }

    /**
     * 放置棋子的方法
     * @param n 第n个皇后
     */
    public void placeQueen(int n) {
        //如果n==8,说明0~7个皇后都放置好了,即一种解法找到了
        if (n == 8) {
            count++;
            // 找到了其中一种解法,位置摆放在queen数组中,可以做其他操作
        } else {
            for (int i = 0; i < queen.length; i++) {
                // 将第n个皇后放置在 i,n 位置上,回溯也是这里开始的,如果n已经没有合适的位置了,那么会回溯到n-1的地方开始,找到n-1这个皇后可以放置的位置,再次递归到n,判断n可以放置的位置
                queen[ n ] = i;
                // 判断这个皇后的位置是否合理,如果合理则开始放下一个皇后,或者说在下一列放置棋子
                if (testAttack(n)) {
                    placeQueen(n + 1);
                }
            }
        }
    }

    /**
     * 判断当前棋子与放置好的棋子有没有冲突
     * @param n 第n个皇后,也代表第n列
     * @return
     */
    public boolean testAttack(int n) {
        for (int i = 0; i < n; i++) {
            //如果棋子在同一行上或者在同一斜线上则返回false
            if (queen[ i ] == queen[ n ] || Math.abs(n - i) == Math.abs(queen[ i ] - queen[ n ])) {
                return false;
            }
        }
        return true;
    }
    
}

2、由8皇后问题衍生的n皇后问题:LeetCode第51题(困难)

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

提示:

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

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

这里只需要将8皇后的代码修改一下即可

class Solution {

    int[] queens;
    List<List<String>> ans=new ArrayList<>();
    
    public List<List<String>> solveNQueens(int n) {
        queens=new int[n];
        placeQueen(0);
        return ans;
    }
    
    /**
     * 放置棋子的方法
     */
    public void  placeQueen(int n){
        if(n==queens.length){
            addRes();
        }else{
            for(int i=0;i<queens.length;i++){
                queens[n]=i;
                if(testAttack(n)){
                    placeQueen(n+1);
                }
            }
        }
    }
    /**
     * 得到解法后的处理
     */
    public void addRes(){
        List<String> list=new ArrayList<>();
        char[] chs=new char[queens.length];
        for(int i=0;i<queens.length;i++){
            chs[i]='.';
        }
        for(int i=0;i<queens.length;i++){
            chs[queens[i]]='Q';
            list.add(new String(chs));
            chs[queens[i]]='.';
        }
        ans.add(list);
    }
    /**
     * 判断当前棋子与放置好的棋子有没有冲突
     */
    public boolean testAttack(int n){
        for(int i=0;i<n;i++){
            if(queens[n]==queens[i] || Math.abs(i-n)==Math.abs(queens[i]-queens[n])){
                return false;
            }
        }
        return true;
    }
}