方法一
实现思路
首先理解一个皇后的攻击范围(以四皇后为例,红色箭头为皇后攻击范围)
在攻击范围内的格子是不能放别的皇后的
放皇后的思路:
一、 遍历每一行,在每一行中特定列中放置皇后
这么遍历的好处是肯定保证了同一行中只有一个皇后,将问题转换为了选择不发生冲突的列
二、 在没有可以选择的列中放置皇后
这里可以选择的列要满足两个条件:
- 本列之前没有放过皇后
- 放在此行此列(x,y)不在其他皇后(xi,yi)的对角线上
- 两个位置(x1,y1),(x2,y2)在同一个斜线满足的条件为
abs(x1-x2)=abs(y1-y2)
- 两个位置(x1,y1),(x2,y2)在同一个斜线满足的条件为
三、 放置的过程中,发现往下走并不可以,回到第二步新选择一个列
四、 发现皇后全部放置完,且符合规则,保存结果
求解实现的函数
- 判断当前位置是否和之前的皇后在同一位置上
- 回溯生成各个皇后放置的位置
- 将位置信息转换为题目要求的字符串格式
实现代码
class Solution {
public:
bool attack(int x,int y,vector<int> &put){
for(int i=0;i<put.size();i++){
if(put[i]!=-1&&abs(x-i)==abs(y-put[i])) return true;
}
return false;
}
void generate(int x,int n,vector<int> &put,vector<int> & book,vector<vector<int>> &re){
//x,y均从0开始
if(x==n){
re.push_back(put);
return;
}
for(int y=0;y<n;y++){
if(!book[y]&&(!attack(x,y,put))){
book[y]=1;
put[x]=y;
generate(x+1,n,put,book,re);
book[y]=0;
put[x]=-1;
}
}
}
void int2string(int n,vector<vector<int>> &re,vector<vector<string>> &re_string){
for(int i=0;i<re.size();i++){
vector<string> vs;
for(int j=0;j<n;j++)
{
string s(n,'.');
s[re[i][j]]='Q';
vs.push_back(s);
}
re_string.push_back(vs);
}
}
vector<vector<string>> solveNQueens(int n) {
vector<int> put(n,-1);
vector<int> book(n,0);
vector<vector<int>> re;
vector<vector<string>> re_string;
generate(0,n,put,book,re);
int2string(n,re,re_string);
return re_string;
}
};
提交结果及分析
如果从状态空间解的角度来看的话,N皇后问题的时间复杂度是O(N!),但在算法的实现过程中,并非真的第一个皇后放置后,下一个皇后尝试N-1个位置,经历了一个剪枝的过程,在已放置皇后攻击范围内的位置是不会被尝试的
方法二
实现思路
总的思路还是回溯,不同于在于放置皇后之后,多了一步标记不能被放置的位置(为实现这个单独实现了一个函数)
实现代码
标记不能走的位置
总结
我觉得我的第一种方法,要比参考别人的第二种方法效率更高,第二种方法多了一步用mark标记的过程,我取而代之的方法只需要利用下标根据斜线的规律计算一下即可,效率及实现更简洁高效