The n-queens puzzle is the problem of placing n queens on an n×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.."] ]
首先要科普下在西洋棋中queen的角色。对于小时候玩过西洋棋后来就忘光的我来说,看到题目我真的不明白什么叫Queen attack each other。
Queen这个棋子只能竖着来,或者横着来,或者斜着来。基本可以把它理解为愣头青。所以要保证没有任何两个Queen相互攻击,就需要同时满足任意一行或一列不能出现两个Queen,任意一条斜线上也不能出现两个Queen。因此,考虑最基本的方法就是去遍历这个n*n的棋盘,看看如何摆放这n个Queen可以满足要求。
那么如何去遍历呢,想到一种直观的方法。用一个长为n整数数组int[] loc表示棋盘上Queen的位置。具体来说,res的每个元素的index表示表示所处的行,而该index中的值表示改行Queen所在的位置。例如loc[0] = 5表示第0行Queen在第5列。这样的数据结构可以保证任何一行只有一个Queen,因此我们只需要再保证这个Queen所在列和斜线上没有其他Queen,就能说明这个Queen是合法的。利用深度优先算法,不断深入遍历每一行,即,如果当前行中Queen所处位置合法,就递归的遍历下一行,检查是否合法。若所有行均合法,则返回。算法伪代码如下:
main_function(n) :
初始化loc[n]
初始化结果集res
dfs(res, loc, 0, n)
return res
dfs(res,loc,row,n)
if row == n:
return
else
for i in n:
loc[row] = i
if loc[row]合法:
dfs(res,loc,row+1,n)
根据以上伪代码,Java代码实现如下:
public class Solution {
public List<String[]> solveNQueens(int n) {
ArrayList<String[]> res = new ArrayList<String[]>();
int[] loc = new int[n];//loc表示皇后所在位置,其每一个元素代表一行。比如loc = {1,3,2,0}表示在第一行,皇后在第一列,在第二行,皇后在第3列,以此类推。
dfs(res, loc, 0, n);//从第0行开始深度优先搜索
return res;
}
private void dfs(ArrayList<String[]> res, int[] loc, int row, int n) {
if (row == n) {
//如果行数超过n了,说明所有行上的皇后都满足要求,因此用printBoard方法将得到的一种解决方案加入解集res当中
printBoard(res, loc, n);
} else {
for (int col = 0; col < n; col++) {
//遍历每一行中的每一列
loc[row] = col;
if (isValid(loc, row)) {
//如果改行中皇后处于的位置是合法的,则深度优先搜索下一行row+1
dfs(res, loc, row + 1, n);
}
}
}
}
private boolean isValid(int[] loc, int row) {
for (int i = 0; i < row; i++) {
if (loc[row] == loc[i] || Math.abs(loc[row] - loc[i]) == (row - i)) {
//到当前行row之前的所有行,判断是否有某行的皇后与当前行皇后处于同一列,若有,返回false不合法
//到当前行row之前的所有行,判断是否有某行的皇后与当前行皇后处于斜线上(等腰三角形),若有,返回false不合法
return false;
}
}
return true;
}
private void printBoard(ArrayList<String[]> res, int[] loc, int n) {
String[] ans = new String[n];//ans的每个元素表示某一种solution中的某一行
for (int i = 0; i < n; i++) {
String row = new String();
for (int j = 0; j < n; j++) {
if (loc[i] == j)
row += "Q";
else
row += ".";
}
ans[i] = row;
}
res.add(ans);
}
}
后记补充:后来才发现这就是NP问题。NP问题的一般解决思路就是递归遍历所有可能性,如果当前可能性合法,则递归继续,如果不合法则返回上一级递归。验证当前可能性是否合法需要一个多项式时间的验证程序。所以是NP问题。