leetcode--n_queens&&n_queens_II

33 篇文章 0 订阅
27 篇文章 0 订阅

n_queens_II

题意: 给定n个皇后和一个n*n的棋盘,找到有多少种相容的放置方法。
条件: 相容的意思是皇后相互之间不能攻击,根据国际象棋规定,皇后可以攻击它所在位置的行、列和斜对角线中所有的棋子,所以要能够实现相容,每一个皇后与前面的所有皇后不能在同一行、同一列、同一对角线。
举例

下图是8皇后问题的一个可行解。

分析: 解决n皇后问题,用到的方法即为回溯。回溯是五大常用算法之一,其余四个分别为:分治、贪心、动态规划和分支限界。回溯法首先需要定义一颗解空间树(其实如何得到解空间树本身也是一个需要讨论的问题); 其次会有一定的约束条件,根据约束条件可以进行剪枝,删除无用的; 遍历解空间树采用深度优先策略,直到叶子节点也遍历过,则从根到叶子节点的路径即为一个可行解。找到一组可行解或者不满足约束条件时,都要进行回溯,即返回到上一个状态继续遍历。当遍历过整个解空间树后算法终止。

对于n皇后问题,我们一般按行来放置皇后,即一行只能有一个皇后。那么可以有 nn 种放置方法,即3皇后问题就可以用27种不同的放置方法,那么对应的解空间树也就有这么多种状态。那有这么多种状态不就是个np难的问题了?其实不然,因为有约束条件!n皇后问题约束条件即为不能在一列或者斜对角线,不满足则剪枝,由约束条件可以剪去绝大多数的情况,从而极大的提升算法效率。

n皇后是个非常经典的问题,读者需要了解回溯的概念和两种实现方式(递归和迭代),在这里我给出一种递归的思路。

代码

//将棋盘看成一个二维数组
public int total = 0;   //全局变量,表示总共有多少种放置方法
    public int totalNQueens(int n) {  
        if(n <= 0) {
            return 0;
        }else if(n == 1){
            return 1;
        }else{
            for(int i = 0; i < n; i++){
                Map<Integer, Integer> map = new HashMap<>();
                map.put(0, i);    //将二维数组的坐标放到map中,横坐标为key,纵坐标为value
                totalNQueen(n, 1, map);
            }
            return total;
        }
    }

    //递归实现回溯
    public int totalNQueen(int n, int row, Map<Integer, Integer> map){  //row表示层数
        if(row >= n) return total + 1;  //如果遍历过解空间树的叶子节点,说明该方法可行,将结果加1
        else{
            for(int i = 0; i < n; i++){
                boolean satisfied = true;
                for(Map.Entry<Integer, Integer> entry : map.entrySet()){
                    int key = entry.getKey();
                    int value = entry.getValue();
                    if(value == i || Math.abs(i - value) == (row - key)){   //判断该节点是否满足约束
                        satisfied = false;                                  //不满足约束直接回溯
                        break;
                    }
                }
                if(satisfied){                                              //满足约束继续找下一层
                    map.put(row, i);
                    total = totalNQueen(n, row + 1, map);
                    map.remove(row);
                }   
            }
            return total;
        }
    }

源程序下载

再提供一个执行效率更高的版本,其实思路都是一样的,不过时间和空间复杂度高一些

public class Solution {

    int solutions;
    int[] points;    //index is m, value is n, -1 means unknown

    public int totalNQueens(int n) {
        points = new int[n];
        dfs(0,n);
        return solutions;
    }
    //row 进行dfs的行数 n=n
    public boolean dfs(int row,int n) {
        if(row == n) {
            solutions++;
            return true;
        }
        for(int i = 0;i < n;i++) {
            if(!check(row,i)) continue;
            points[row] = i;
            dfs(row+1,n);
        }
        return false;
    }
    boolean check(int row,int i) {
        for(int p = 0;p < row;p++) {
            if(points[p] == i)
                return false;
            if(row-p == points[p]-i)
                return false;
            if(p-row == points[p]-i)
                return false;
        }
        return true;
    }
}

n_queens

题意: 该问题要求在II的基础上输出,放置的地方用’Q’,空的地方用’.’表示。
举例: 4皇后问题有2个不同解,如下

[
  [“.Q..”, // Solution 1
“…Q”,
“Q…”,
“..Q.”],
  [“..Q.”, // Solution 2
“Q…”,
“…Q”,
“.Q..”]
]

分析: 该题只需要在II的基础上稍作修改即可,大体思路还是一致的,只不过在满足条件时不是计算个数,而是要将可行解加入到一个ArrayList中,最后返回该ArrayList即可。

代码

public ArrayList<String[]> solveNQueens(int n) {
        ArrayList<String[]> result = new ArrayList<>(); //返回的结果

        String[] strs = new String[n];  //初始化String的数组,起始都是“.”
        char[] chs = new char[n];
        for(int i = 0; i < n; i++){
            chs[i] = '.';
        }
        String str = String.valueOf(chs);
        for(int i = 0; i < n; i++){
            strs[i] = str;
        }
        Map<Integer, Integer> map = new HashMap<>();
        result = solveNQueens(n, 0, map, result, strs);
        return result;
    }

public ArrayList<String[]> solveNQueens(int n, int row, Map<Integer, Integer> map, ArrayList<String[]> result, String[] strs){
        if(row >= n){
            for(Map.Entry<Integer, Integer> entry : map.entrySet()){
                int key = entry.getKey();
                int value = entry.getValue();
                char[] chs = strs[key].toCharArray();
                chs[value] = 'Q';
                strs[key] = String.valueOf(chs);
            }
            String[] temps = new String[n];
            for(int i = 0; i < temps.length; i++){
                temps[i] = strs[i];
            }
            result.add(temps);
            for(int i = 0; i < n; i++){
                strs[i] = strs[i].replace("Q", ".");
            }
            return result;  //如果遍历过解空间树的叶子节点,说明该方法可行
        }
        else{
            for(int i = 0; i < n; i++){
                boolean satisfied = true;
                for(Map.Entry<Integer, Integer> entry : map.entrySet()){
                    int key = entry.getKey();
                    int value = entry.getValue();
                    if(value == i || Math.abs(i - value) == (row - key)){   //判断该节点是否满足约束
                        satisfied = false;                                  //不满足约束直接回溯
                        break;
                    }
                }
                if(satisfied){                                              //满足约束继续找下一层
                    map.put(row, i);
                    result = solveNQueens(n, row + 1, map, result, strs);
                    map.remove(row);                    
                }   
            }
            return result;
        }
    }

源程序下载

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值