N-Queens
Given an integer n, return all distinct solutions to the n-queens puzzle.
http://www.lintcode.com/en/problem/n-queens/
Discussion
N-Queens N皇后问题,非常经典的DFS问题。
用一个一位数组来存放当前皇后的状态。假设数组为int state[n], state[i]表示第 i 行皇后所在的列。那么在新的一行 k 放置一个皇后后:
- 判断列是否冲突,只需要看state数组中state[0…k-1] 是否有和state[k]相等;
- 判断对角线是否冲突:如果两个皇后在同一对角线,那么|row1-row2| = |column1 - column2|,(row1,column1),(row2,column2)分别为冲突的两个皇后的位置
Solution
Recursion
// Time: O(n * n!)
// Space: O(n)
class Solution {
public:
/**
* Get all distinct N-Queen solutions
* @param n: The number of queens
* @return: All distinct solutions
* For example, A string '...Q' shows a queen on forth position
*/
vector<vector<string> > solveNQueens(int n) {
// write your code here
vector<vector<string> > result;
if(n==0) return result;
vector<int> state(n, -1);//存的是各行queen放在哪一列
dfs(0, state, result);
return result;
}
private:
void dfs(int row, vector<int> &state, vector<vector<string> > &solutions) {
int n = state.size();
if(row == n) {//output result
//vector<vector<string> > tmpSol(n, vector<string>(n, '.'));
vector<string> tmpSol(n, string(n,'.'));
for(int i=0; i<n; i++) {
tmpSol[i][state[i]] = 'Q';
}
solutions.push_back(tmpSol);
}
for(int col=0; col<n; col++) {
if(isSafe(state, row, col)) {
state[row] = col;
dfs(row+1, state, solutions);
state[row] = -1;
}
}
}
bool isSafe(vector<int> state, int row, int col) {
for(int i=0; i<row; i++) {
if(state[i] == col || abs(row-i) == abs(col-state[i]))
return false;
}
return true;
}
};
Iteration的方法比较难。。。
还是用数组int state[n], state[i]表示第 i 行皇后所在的列,初始化为-1表示都还没有开始放, 从第row=0行开始放, 有四种情况需要考虑:
- 已经放到row==n了,说明已经放了n个queen了,得到一个可行解了,这时候要输出可行解,并且,注意了!!! 还要继续扩展求解下一个solution,因为题目要求列出所有solution。扩展方法是回到上一行,将上一行的queen放到下一个位置 --- row--, state[row]++, 继续判断。
- 还没有放到row==n,已经试到最后一列了(state[row]==n),这时候说明在row这一行,所有列都试过了,那么就要重这一行的queen,回到上一行(row--),(这时候row是可能变成-1的),并且将上一行的queen位置加1再继续试。
- 还没有到最后一列,那就检验这一列能不能放(或者还是-1还没开始放),不能放就继续试下一列(还是在这一行,上面两种情况都是要回到上一行的)。
- 如果这一列能放,那就开始下一行。
这四种情况在一个大的循环里。长话短说就是当某个位置不能放就试下一个位置(下一列),反之若能放,就move到下一行,但是在试之前(不是move到下一行之前)要看一看是不是已经到达最后一行了(输出solution),还要看一看是不是已经放到最后一列了。这两种情况都是要回到上一行的。
class Solution {
public:
/**
* Get all distinct N-Queen solutions
* @param n: The number of queens
* @return: All distinct solutions
* For example, A string '...Q' shows a queen on forth position
*/
vector<vector<string> > solveNQueens(int n) {
// write your code here
vector<vector<string> > result;
if(n==0) return result;
vector<int> state(n, -1);
int row = 0;
while (true) {
if(row == n) {//达到最后一行了,输出结果
vector<string> tmpSol(n, string(n,'.'));
for(int i=0; i<n; i++) {
tmpSol[i][state[i]] = 'Q';
}
result.push_back(tmpSol);
row--;//继续扩展
state[row]++;
} else if(state[row] == n) {//到达最后一列了,回到上一行,继续
state[row] = -1;//要回上一行了,所以当前行queen要清除掉重新来过
row--;
if(row==-1) break;
state[row]++;
} else if(state[row] == -1 || isSafe(state, row, state[row]) == false) {
state[row]++;//这一列失败了,继续试下一列
} else {//一切正常,继续下一行
row++;
}
}
return result;
}
bool isSafe(vector<int> &state, int row, int col) {
for(int i=0; i<row; i++) {
if(state[i] == col || row-i == abs(col-state[i])) {
return false;
}
}
return true;
}
};
上面的itereation的方法确实比较难想出来,有没有更一般的办法呢?
把递归改成迭代,就需要用一些变量来记录函数执行时的状态。在八皇后问题中,函数的状态和行数是息息相关的,每次递归都是由一行进入下一行,那么用一个变量把把行数记录下来,迭代的时候,以这个变量为序进行操作就可以了。具体如下:
- 在每一行开始的时候,state[row]++,意思是说从下一列(记为第COL列)开始试(初始化时是-1嘛,所以第一次的时候刚好是从第0列开始试。
- 从第COL列开始,找到一个新的能放皇后的列,记为newCol。
- 如果没找到,意味着本行结束,回溯到上一行
- 如果找到了
4.1 如果是最后一行了,那么输出结果,回到上一行
4.2 要是还没到最后一行,继续下一行,同时状态置-1
class Solution {
public:
/**
* Get all distinct N-Queen solutions
* @param n: The number of queens
* @return: All distinct solutions
* For example, A string '...Q' shows a queen on forth position
*/
vector<vector<string> > solveNQueens(int n) {
// write your code here
vector<vector<string> > result;
if(n<=0) return result;
vector<int> state (n, -1);
int row = 0;
while(row>=0) {
bool iFound = false;
state[row] ++;
for(int col=state[row]; col<n; col++) {
if(isSafe(state, row, col)) {
state[row] = col;
iFound = true;
break;
}
}
if(iFound) {
if(row == n-1) {
outputsolution(result, state);
row--;
//state[row]++;
} else {
row++;
state[row] = -1;
}
} else {
row--;
}
}
return result;
}
void outputsolution(vector<vector<string> > &solutions, vector<int> state) {
int n = state.size();
vector<string> sol(n, string(n, '.'));
for(int i=0; i<n; i++) {
sol[i][state[i]] = 'Q';
}
solutions.push_back(sol);
}
bool isSafe(vector<int> state, int row, int col) {
int n = state.size();
for(int i=0; i<row; i++) {
if(state[i] == col || row-i == abs(col-state[i])) {
return false;
}
}
return true;
}
};