标签
这题对我而言还是很有难度的,想了半天没想到解决方法,在网上看了大神们的答案码了出来,下面梳理一下我自己的理解。
一.递归方法
思路:递归方式--回溯算法
这个回溯算法相当于试探法,沿着一条路径走下去,如果走不通返回上一个节点继续走,直到穷举所有路径。
对于N皇后的放置,要求皇后之间不能彼此攻击,即任意两个皇后不能在同一行、同一列、同一对角线上。棋盘相当于一个二维数组。
1 判断传入位置是否超出行最大值(n-1),是的话说明最后一行已经处理好,即已经找到一个解决方案,将该解决方案(字符串数组)push到结果中;否,转到2;
2 判断传入位置能否放Q(与已经放好Q的位置对比,任意两个皇后不能在同一行、同一列、同一对角线上),能,转到3;不能,转到4;
3 该位置字符值置为Q,继续判断下一行;
4 传入当前行的下一个位置(列索引+1),转到第2步。
判断传入位置能否放Q可以单独定义一个bool类型的函数,可以对棋盘行、列分别检索,也可以参考博客中的降维方法:把棋盘存储为一个N维数组a[N],数组中第i个元素的值代表第i行的皇后位置,这样便可以把问题的空间规模压缩为一维O(N),在判断是否冲突时也很简单,首先每行只有一个皇后,且在数组中只占据一个元素的位置,行冲突就不存在了,其次是列冲突,判断一下是否有a[i]与当前要放置皇后的列j相等即可。至于斜线冲突,通过观察可以发现所有在斜线上冲突的皇后的位置都有规律即它们所在的行列互减的绝对值相等,即| row – i | = | col – a[i] | 。这样某个位置是否可以放置皇后的问题已经解决。
转自此博客
AC代码:
class Solution {
public:
/*
* @param n: The number of queens
* @return: All distinct solutions
*/
vector<vector<string>> solveNQueens(int n) {
// write your code here
vector<vector<string>> result;
if (n<=0)
{
return result;
}
int * position=new int[n];
for (int i=0;i<n;i++)
{
position[i]=-1;
}
int row=0;
placeQueen(result,row,position,n);
delete []position;
return result;
}
void placeQueen(vector<vector<string>> &result,int row,int * position,int n)
{
if (row==n) //最后一行处理完,寻找到一个解决方案,将其字符串数组化push到结果中;
{
string str(n,‘.‘);
vector<string> temp(n,str);
for (int i=0;i<n;i++)
{
temp[i][position[i]]=‘Q‘;
}
result.push_back(temp);
}
else
{
for (int j=0;j<n;j++)//寻找该行可以放置Q的列;
{
if (CanPlaceQ(row,j,position,n))//找到,则继续寻找下一行;
{
position[row]=j;
placeQueen(result,row+1,position,n);
}
//找不到,j++,寻找该行的下一列;
}
}
}
bool CanPlaceQ(int row,int col,int * position,int n)
{
for (int i=0;i<row;i++)
{
if (position[i]==col||abs(row-i)==abs(col-position[i]))//判断是否在同一列或同一对角线;
{
return false;
}
}
return true;
}
};
递归方法其他参考:https://blog.csdn.net/sinat_26230689/article/details/52206498 这个代码我看了几遍还是很懵……智商捉急,先把他的思路copy过来:
【解题思路】
深度遍历+回溯。
1. 从上到下,从左到右,判断某个位置是否可以放皇后,可以放,转2,不可以,转3;
2. 放置皇后,并判断是否已经放置N个皇后,如果是,记录结果并回溯(寻找其他解决方案);否则转1,递归判断下一行能否放置皇后;
3. 判断本行下一列是否可以放置皇后。如果本列无法放置皇后,剪枝;否则查看下一列能否放置皇后。
即,可以放置,就往下找;放不了,就往回看,拜托上层变一变,看能不能继续往下找,直到第一层都试过最后一列的位置,程序结束。
由于需要记录所有可行结果并输出,在每次得到可行结果时,将当前结果保存,并将Q还原为".",方便回溯。
二、非递归方法
思路:1、遍历棋盘的行,寻找可以放置Q的列,找到就记录位置(行索引对应的列),判断下一行(注意列置0,因为下一行应从头开始寻找);
2、如果在当前行找不到可以放置Q的列,应该回溯到上一行,从上一行可以放Q的列的后一位开始,同时该列置为-1(未存放Q的状态)。若上一行也找不到位置就继续回溯到上上一行,直到找到可以放Q的位置。如果回溯到第一行也无法找到放Q的位置,说明已经找到所有的解,终止程序;
3、如果放置Q的行是最后一行,说明找到一个解决方案,将其转成字符串数组push到结果中,此时应该继续寻找下一个解决方案,即将当前位置放置Q的状态设置成未存放,从当前位置下一列开始继续寻找。
非递归方法的一个重要问题时何时回溯及如何回溯的问题。程序首先对N行中的每一行进行探测,寻找该行中可以放置皇后的位置,具体方法是对该行的每一列进行探测,看是否可以放置皇后,如果可以,则在该列放置一个皇后,然后继续探测下一行的皇后位置。如果已经探测完所有的列都没有找到可以放置皇后的列,此时就应该回溯,把上一行皇后的位置往后移一列,如果上一行皇后移动后也找不到位置,则继续回溯直至某一行找到皇后的位置或回溯到第一行,如果第一行皇后也无法找到可以放置皇后的位置,则说明已经找到所有的解程序终止。如果该行已经是最后一行,则探测完该行后,如果找到放置皇后的位置,则说明找到一个结果,打印出来。但是此时并不能再此处结束程序,因为我们要找的是所有N皇后问题所有的解,此时应该清除该行的皇后,从当前放置皇后列数的下一列继续探测。
转自此博客
AC代码:
class Solution {
public:
/*
* @param n: The number of queens
* @return: All distinct solutions
*/
bool CanPlaceQ(int row,int col,int * position,int n)
{
for (int i=0;i<row;i++)
{
if (position[i]==col||abs(row-i)==abs(col-position[i]))//判断是否在同一列或同一对角线;
{
return false;
}
}
return true;
}
void placeQueen(vector<vector<string>> &result,int row,int * position,int n)
{
int i=0,j=0;
while(i<n)
{
while(j<n)
{
if (CanPlaceQ(i,j,position,n))
{
position[i]=j;
j=0;//下一行判断时列从头开始;
break; //直接判断下一行;
}
else
{
j++;//当前行当前列无法放Q,判断当前行下一列;
}
}
if (position[i]==-1)//当前行没有可以放Q的位置,回溯;
{
if (i==0)//回溯到第一行也无解,说明找到所有解,退出程序;
{
break;
}
--i;//否则回溯到上一行;
j=position[i]+1;//从上一行可以放Q的下一位开始判断;
position[i]=-1;//注意清空上一行位置!!;
continue;
}
if (i==n-1) //最后一行判断完且找到放Q位置,将当前解决方案放入结果中;
{
string str(n,‘.‘);
vector<string> temp(n,str);
for (int k=0;k<n;k++)
{
temp[k][position[k]]=‘Q‘;
}
result.push_back(temp);
j=position[i]+1;//此时不能结束,要找下一个解决方案,继续判断当前行下一个位置是否符合要求;
position[i]=-1;//注意当前位置的状态要置-1;
continue;
}
i++;
}
}
vector<vector<string>> solveNQueens(int n)
{
vector<vector<string>> result;
if (n<=0)
{
return result;
}
int * position=new int[n];
for (int i=0;i<n;i++)
{
position[i]=-1;
}
int row=0;
placeQueen(result,row,position,n);
delete []position;
return result;
}
};