N-Queens

来源:互联网 发布:淘宝装修图片加热点 编辑:程序博客网 时间:2024/05/23 15:06

这个题花了我很长时间。

原题:

The n-queens puzzle is the problem of placing n queens on ann×n chessboard such that no two queens attack each other.

Given an integer n, return all distinct solutions to the n-queens puzzle.

Each solution contains a distinct board configuration of the n-queens' placement, where'Q' and '.' both indicate a queen and an empty space respectively.

For example,
There exist two distinct solutions to the 4-queens puzzle:

[ [".Q..",  // Solution 1  "...Q",  "Q...",  "..Q."], ["..Q.",  // Solution 2  "Q...",  "...Q",  ".Q.."]]
是比较经典的n皇后问题(听说很经典,以前没玩过)。规则是nxn的方格里每行每列都有一个皇后(其实每行就限制了每列),她们不在同一行、同一列或者同一个斜线上。一开始只以为不在同一行同一列就行,费了力气写了一个函数。发现问题后添加了最后一项规则,按照以前的思路:它是从n = 4一级一级往上更新答案。但是结果集不完整。后来想到了用类似soduku的回溯方法解决这个问题。
思考过程:
用回溯的思想后,先直接对List<List<String>>进行操作,最后终于能解决,但是对List<List<String>>操作太费时间了,最后果然超时了:
//超时算法    public List<List<String>> solveNQueensTLE(int n) {        List<List<String>> ret = new ArrayList<>();        List<String> strings = new ArrayList<>();        strings.add("");        backTrackTLE(ret,strings,0,0,n);        return ret;    }    public void backTrackTLE(List<List<String>> ret,List<String> subRet,int row,int column,int n){        if (column == n)            if (!subRet.get(row).contains("Q")) return;            else if (row == n - 1){                ret.add(subRet);return;            }            else {                row++;column = 0;                subRet.add("");            }        String str = subRet.get(row);        String s = str + 'Q';        List<String> newSubRet = new ArrayList<>();        newSubRet.addAll(subRet);        if (canPutQTLE(subRet,row,column,n)){            subRet.remove(row);subRet.add(s);            backTrackTLE(ret,subRet,row,n,n);        }        String s1 = str + '.';        newSubRet.remove(row);newSubRet.add(s1);        backTrackTLE(ret,newSubRet,row,column + 1,n);    }    public boolean canPutQTLE(List<String> subRet,int row,int column,int n){        for (int i = 0;i < row;i++)            if (subRet.get(i).charAt(column) == 'Q') return false;        for (int i = 1;i <= Math.min(row,column);i++)            if (subRet.get(row - i).charAt(column - i) == 'Q') return false;        for (int i = 0;i <= Math.min(row,n - 1 - column);i++)            if (subRet.get(row - i).charAt(column + i) == 'Q') return false;        if (column > 0 && subRet.get(row).contains("Q")) return false;        return true;    }

后来想到用一个一维数组,存放第i行皇后的位置。比如subRet[3] = 2,表示第三行皇后在第二个位置上。思路都是差不多的,不过对数组操作、比较终究是快捷的(见到有强者用位运算记录皇后位置,觉得很厉害,但未能领会)。
解题思路:
详见代码注释。
结果代码:
public List<List<String>> solveNQueens(int n) {        int[] subRet = new int[n];//初始化数组,每个数组代表结果的一个解集。数组初始化为零,为了少操作,subRet[i] = 0表示第i行没有皇后,而不是第一个位置上是皇后,subRet[i] = 1表示第一个位置上是皇后        List<List<String>> ret = new ArrayList<>();        backTrack(ret,subRet,0,1,n);//column最小为1        return ret;    }    public void backTrack(List<List<String>> ret,int[] subRet,int row,int column,int n){        if (column == n + 1)//column越界的情况            if (subRet[row] == 0) return;//说明这一行没有皇后,这个解集是失败的,不再用。return            else if (row == n - 1){//解集已经补全了                addIntoResult(ret,subRet,n);//将数组转化为List<List<String>>                return;            }            else {                row++;column = 1;//以上情况都不属于,那么变到下一行。            }            int[] newSubRet = new int[n];            newSubRet = Arrays.copyOf(subRet,n);//复制一个解集,用于在当前位置放'Q'        if (canPutQ(newSubRet,row,column))//判断是否可以放'Q'            backTrack(ret,newSubRet,row,n + 1,n);//如果能,调到下一行        backTrack(ret,subRet,row,column + 1,n);//不能放的情况,向后走    }    public void addIntoResult(List<List<String>> ret,int[] subRet,int n){        List<String> sub = new ArrayList<>();        for (int i = 0;i < n;i++){            String s = "";            for (int j = 1;j <= n;j++)                if (subRet[i] != j) s += ".";                else s += "Q";            sub.add(s);        }        ret.add(sub);    }    public boolean canPutQ(int[] subRet,int row,int column){//判断此处是否可以放'Q'        if (subRet[row] != 0) return false;//这行有'Q',返回false        for (int i = 0;i < row;i++)            if (subRet[i] == column || Math.abs(subRet[i] - column) == Math.abs(i - row)) return false;//这一列或者斜线上是否有Q        subRet[row] = column;        return true;    }

原创粉丝点击