leetcode51. N-Queens
- 回溯法
- 本质:深度优先(隐式图搜索)
- 这里不用二维数组处理起来更简单,一列一列的去看,去递归
- 决策产生状态,巧妙利用全局标记量记录状态,来剪枝。
- 斜线的处理方法,是直接看行列的和或差,因为斜线方程是x + y = k 和 x - y = k
- 递归出来后面的语句记得恢复状态
- 时间复杂度O(n^n) (暴力法下的状态空间数)
#include <iostream>
#include <vector>
#include <string>
using namespace std;
class Solution {
private:
vector<vector<string> > ans;
bool row[20];
int col[20]; //col[i] 的值表示第i列的皇后在该列什么位置(位置也是从0到n)
bool leftSlash[20]; //左斜线,像中文的捺
bool rightSlash[20]; //右斜线,像中文的撇
/*
斜线,方程是x + y = k 和 x - y = k 所以去查k的值,就知道这条斜线是否能放皇后,也就是下面代码呈现的
leftSlash[index + i] 和 rightSlash[index - i + n - 1]
是因为下标不能为负,所以坐标平移后是 index - i + n - 1
*/
public:
vector<vector<string> > solveNQueens(int n) {
dfs(0, n);
return ans;
}
void dfs(int index, int n){ //index表示现在检查的列
if(index >= n) { //递归结束条件
vector<string> rowAns;
for(int i = 0; i < n; i++){ //存储结果,按行存储
string tmp;
for(int j = 0; j < n; j++){
if(j == col[i])
tmp += "Q";
else
tmp += ".";
}
rowAns.push_back(tmp);
}
ans.push_back(rowAns);
return;
}
// 下面按行去 dfs, 列肯定不会冲突,因为本身涉及了按列参数去递归
for(int i = 0; i < n; i++){
//如果该行能放皇后,且不与斜线冲突,那么才进行里面的递归。剪枝
if(!row[i] && !leftSlash[index + i] && !rightSlash[index - i + n - 1]){
col[index] = i; //让index这列的皇后在i行的位置
row[i] = true;
leftSlash[index + i] = true;
rightSlash[index - i + n - 1] = true;
dfs(index + 1, n);
//递归出来记得用下面几句恢复状态,这样才能再进行别的 dfs 不出错
row[i] = false; // false 状态表示这个位置没有皇后
leftSlash[index + i] = false;
rightSlash[index - i + n - 1] = false;
}
}
}
};
int main(){
int n;
cin >> n;
vector<vector<string> > res;
Solution *test = new Solution();
res = test->solveNQueens(n);
for(int i = 0; i < res.size(); i++){
for(int j = 0; j < res[0].size(); j++){
cout << res[i][j] << endl;
}
cout << endl;
}
cout << res[0].size() << "皇后有" << res.size() << "种摆法" << endl;
return 0;
}
另外,骑士周游(马踏棋盘)问题,可以加上A*启发式优化方法,即改变搜索顺序。
参考 julyedu 图搜索实战班第一课