leetcode--n_queens&&n_queens_II

来源:互联网 发布:南风知我意在线阅读 编辑:程序博客网 时间:2024/05/01 17:30

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
原创粉丝点击