33 N皇后问题

Posted 唐的糖

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了33 N皇后问题相关的知识,希望对你有一定的参考价值。

原题网址:https://www.lintcode.com/zh-cn/old/problem/n-queens/#

n皇后问题是将n个皇后放置在n*n的棋盘上,皇后彼此之间不能相互攻击。

给定一个整数n,返回所有不同的n皇后问题的解决方案。

每个解决方案包含一个明确的n皇后放置布局,其中“Q”和“.”分别表示一个女王和一个空位置。

样例

对于4皇后问题存在两种解决的方案:

[

    [".Q..", // Solution 1

     "...Q",

     "Q...",

     "..Q."],

    ["..Q.", // Solution 2

     "Q...",

     "...Q",

     ".Q.."]

]

挑战 

你能否不使用递归完成?

标签 
 
这题对我而言还是很有难度的,想了半天没想到解决方法,在网上看了大神们的答案码了出来,下面梳理一下我自己的理解。
 
 
          N皇后问题的两个最高效的算法           老实说这篇文里的算法伪码描述看的我很懵比,在N皇后Ⅱ问题中自己再码了一遍代码,总算明白过来,下面简单说说……算法的高级伪码描述,这里用一个N*N的矩阵来存储棋盘:

      1) 算法开始, 清空棋盘,当前行被设为第一行,当前列被设为第一列(即从0,0处开始扫描)

      2) 在当前行,当前列的位置上判断是否满足条件(即保证经过这一点的行,列与斜线上都没有两个皇后),若不满足,跳到第4步

      3) 在当前位置上满足条件的情形:

                 在当前位置放一个皇后,若当前行是最后一行,记录一个解;

                 若当前行不是最后一行,当前行设为下一行, 当前列设为当前行的第一个待测位置;

                 以下两步是当前行是最后一行情况下的执行步骤,即找其他解过程

                 若当前行是最后一行,当前列不是最后一列,当前列设为下一列;

                 若当前行是最后一行,当前列是最后一列,回溯,即清空当前行及以下各行的棋盘,然后,当前行设为上一行,当前列设为当前行的下一个待测位置;

                以上返回到第2步

      4) 在当前位置上不满足条件的情形:

                若当前列不是最后一列,当前列设为下一列,返回到第2步;

                若当前列是最后一列了,回溯,有两种情况:若当前行已经是第一行了(说明找到所有解),算法退出,否则,清空当前行及以下各行的棋盘,然后,当前行设为上一行,当前列设为当前行的下一个待测位置,返回到第2步; 

 
一.递归方法
 
递归方式--回溯算法
 
这个回溯算法相当于试探法,沿着一条路径走下去,如果走不通返回上一个节点继续走,直到穷举所有路径。
 
对于N皇后的放置,要求皇后之间不能彼此攻击,即任意两个皇后不能在同一行、同一列、同一对角线上。棋盘相当于一个二维数组。
 
 程序步骤描述:
1 判断传入位置是否超出行最大值(n-1),是的话说明最后一行已经处理好,即已经找到一个解决方案,将该解决方案(字符串数组)push到结果中;否,转到2;
 
2 判断传入位置能否放Q(与已经放好Q的位置对比,任意两个皇后不能在同一行、同一列、同一对角线上),能,转到3;不能,转到4;
 
3 该位置字符值置为Q,从下一行的列起点开始继续寻找能放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;
}
};

 

 
 
 
 

以上是关于33 N皇后问题的主要内容,如果未能解决你的问题,请参考以下文章

递归--N皇后问题

51. N 皇后

51. N 皇后

51. N 皇后

N皇后问题—初级回溯

八皇后问题求解的C语言程序的实现