对应letecode链接:
51. N 皇后 - 力扣(LeetCode) (leetcode-cn.com)
题目描述:
解题思路:
我们来找规律,先看一下4皇后的问题
比如在下面的4*4的格子里,如果我们在其中一个格子里输入了皇后,那么在这一行这一列和这左右两边的对角线上都不能有皇后。
首先我们在第一行和第一列的位置放一个皇后:那么其对应行和其对角线都不能放皇后:
直至到第二行来。第二行我们就不能在第一列和第二列输入皇后了,因为有冲突了。但我们可以在第3列输入皇后:
第三行我们发现在任何位置输入都会有冲突。这说明我们之前选择的是错误的,再回到上一步,我们发现第二步不光能选择第3列,而且还能选择第4列,既然选择第3列不合适,那我们就选择第4列吧
后面的同理但是在递归回来的时候可以重新旋转之前由于之前放黄后而不能旋转的位置。
现在的难点是如果标记列和对角线了。列和容易我们使用一个unordered_set标记即可,行的话我们在这一行放了皇后之后马上递归到下一层即可但是对角线怎么半呢?感觉有点小复杂其实并不复杂 如果一个皇后放的位置为 i,j那么他的对角线的位置有 i+1,j+1,i+1,j-1,我们发现有一个方向对角线的坐标相减为i-j为定值,另外一个对角线坐标相加为i+j为定值,所以我们只需要使用一个哈希表记录即可。
具体请看
class Solution { public: vector<vector<bool>>visit;//标记某个位置是否放过皇后 unordered_set<int>col;//标记这一列不能放皇后 unordered_set<int>pia;//标记对角线不能放皇后 unordered_set<int>na; int _n;//数量 vector<vector<string>>ans;//记录答案 vector<vector<string>> solveNQueens(int n) { visit.resize(n,vector<bool>(n));//初始化为false; this->_n=n; dfs(0);//递归 return ans; } void dfs(int i) { if(i==_n)//如果已经走到最后一行说明 { ceraterAns();//产生答案了 return; } for(int j=0;j<_n;j++) { if(col.count(j)||pia.count(i+j)||na.count(i-j))//说明被标记过不能够放皇后直接 //continue即可跳过了 { continue; } visit[i][j]=true;//标记这个这个位置已经放过皇后 col.insert(j);//标记这一列不能放皇后 pia.insert(i+j);//对角线标记 na.insert(i-j); dfs(i+1);//这一层放过了直接到下一层即可 visit[i][j]=false;//递归返回之后需要将之间的标记去除 col.erase(j); pia.erase(i+j); na.erase(i-j); } } void ceraterAns() { vector<string>tmp; for(int i=0;i<_n;i++) { string str; for(int j=0;j<_n;j++) { if(visit[i][j]) { str.push_back('Q'); } else { str.push_back('.'); } } tmp.push_back(str); } ans.push_back(tmp); } };
题目描述:
解题思路:
这题和上题基本一模一样,相比上题这题是上题的简单版本。思路上题已经讲过,只需要将上题的代码拷贝过来即可。
对应代码:
class Solution { public: vector<vector<bool>>visit;//标记某个位置是否放过皇后 unordered_set<int>col;//标记这一列不能放皇后 unordered_set<int>pia;//标记对角线不能放皇后 unordered_set<int>na; int _n;//数量 int ans=0; int totalNQueens(int n) { visit.resize(n,vector<bool>(n));//初始化为false; this->_n=n; dfs(0);//递归 return ans; } void dfs(int i) { if(i==_n)//如果已经走到最后一行说明 { ans++;//产生答案了 return; } for(int j=0;j<_n;j++) { if(col.count(j)||pia.count(i+j)||na.count(i-j))//说明被标记过不能够放皇后直接 //continue即可跳过了 { continue; } visit[i][j]=true;//标记这个这个位置已经放过皇后 col.insert(j);//标记这一列不能放皇后 pia.insert(i+j);//对角线标记 na.insert(i-j); dfs(i+1);//这一层放过了直接到下一层即可 visit[i][j]=false;//递归返回之后需要将之间的标记去除 col.erase(j); pia.erase(i+j); na.erase(i-j); } } };
下面是letecode一道重复的题:
题目描述:
对应代码:
class Solution { public: vector<vector<bool>>visit;//标记某个位置是否放过皇后 unordered_set<int>col;//标记这一列不能放皇后 unordered_set<int>pia;//标记对角线不能放皇后 unordered_set<int>na; int _n;//数量 vector<vector<string>>ans;//记录答案 vector<vector<string>> solveNQueens(int n) { visit.resize(n,vector<bool>(n));//初始化为false; this->_n=n; dfs(0);//递归 return ans; } void dfs(int i) { if(i==_n)//如果已经走到最后一行说明 { ceraterAns();//产生答案了 return; } for(int j=0;j<_n;j++) { if(col.count(j)||pia.count(i+j)||na.count(i-j))//说明被标记过不能够放皇后直接 //continue即可跳过了 { continue; } visit[i][j]=true;//标记这个这个位置已经放过皇后 col.insert(j);//标记这一列不能放皇后 pia.insert(i+j);//对角线标记 na.insert(i-j); dfs(i+1);//这一层放过了直接到下一层即可 visit[i][j]=false;//递归返回之后需要将之间的标记去除 col.erase(j); pia.erase(i+j); na.erase(i-j); } } void ceraterAns() { vector<string>tmp; for(int i=0;i<_n;i++) { string str; for(int j=0;j<_n;j++) { if(visit[i][j]) { str.push_back('Q'); } else { str.push_back('.'); } } tmp.push_back(str); } ans.push_back(tmp); } };