HDU(搜索专题) 1000 N皇后问题(深度优先搜索DFS)解题报告

        前几天一直在忙一些事情,所以一直没来得及开始这个搜索专题的训练,今天做了下这个专题的第一题,皇后问题在我没有开始接受Axie的算法低强度训练前,就早有耳闻了,但一直不知道是什么类型的题目,今天一看,原来是搜索题,还是入门的深搜题,关于题目的意思recwert在这就不解释了。直接讲讲思路吧。

        按照深度优先搜索的理念,应该是从上到下搜索(如果你硬要说可以从下到上。。但是仔细想想其实是一个意思。。)在二叉树的DFS中,我们是假定了左孩子优先,所以在这题,我们也可以假定行优先(行优先和列优先是一个结果,或者说根本就说一个思路)。

        让我们先画一个棋盘:(4*4棋盘,不多也不少)

( 0,0) ( 0,1) ( 0,2) ( 0,3)
( 1,0) ( 1,1) ( 1,2) ( 1,3)
( 2,0) ( 2,1) ( 2,2) ( 2,3)
( 3,0) ( 3,1) ( 3,2) ( 3,3)

       我们需要开一个保存当前已经搜索的进度,用一个一维数组即可:queen[4], queen[i]代表第i行选择的queen[i]列。如下代码表示了该题需要存的所有数据

int queen[MAXN];   //皇后位置的选取情况
int sum = 0;       //迭代结果变量
int result[MAXN] = {0}; //保存结果变量,防止测试量多的情况下发生超市
int m;             //题目输入变量,皇后数(棋盘边长)

接下来我们开始DFS, 从第一行开始搜索,每行的搜索按照从左到右的顺序,那第一行搜索结果应该是这样的。(红色为选中)

 

( 0,0) ( 0,1) ( 0,2) ( 0,3)
( 1,0) ( 1,1) ( 1,2) ( 1,3)
( 2,0) ( 2,1) ( 2,2) ( 2,3)
( 3,0) ( 3,1) ( 3,2) ( 3,3)


接下来搜索第二行

结果应该是这样的:(至于为什么是这个结果根据题意就可以明白了)

( 0,0) ( 0,1) ( 0,2) ( 0,3)
( 1,0) ( 1,1) ( 1,2) ( 1,3)
( 2,0) ( 2,1) ( 2,2) ( 2,3)
( 3,0) ( 3,1) ( 3,2) ( 3,3)

在接下来搜索第三行的时候会发现不管选择哪一个都是不行的。通过什么方法来判断行或者不行呢。写一个check()方法来检测, 参数n表示检测第n行

bool check( int n ){
    for( int i = 0; i < n; i++ ){
        //检查在第n行之前是否有在同一列或者45度对角线上
        if( queen[i] == queen[n] || abs( queen[i] - queen[n]) == ( n - i )){
            return false;
        }
    }
    return true;
}

在发现此次搜索失败后,应该做什么呢。呵呵,当然是什么都不做啦。那具体我们如何实现这个dfs呢。看看下面的代码:

void put( int n ){
    //从第n行开始dfs,搜索第n行的m列
    for( int i = 0; i < m; i++ ){
        queen[n] = i;
        //检测成功
        if( check(n) ){
            //搜索到最后一行了
            if( n == m - 1 ){
                sum++;
            }
            else{
                put( n + 1);
            }
        }
    }
}

如果皇后数量为4,那么这里的m应该为4,put函数中初始传入的参数为0,表示从第0行开始搜索,对第0行的没一列都要搜索,如果搜索成功了但没到最后一行,就继续搜索下一行,如果到最后一行了,sum自增,如此递归,就能将结果搜索出来了。我们来看一个可以满足条件的结果

搜索第0行:

0,0) ( 0,1) ( 0,2) ( 0,3)
( 1,0) ( 1,1) ( 1,2) ( 1,3)
( 2,0) ( 2,1) ( 2,2) ( 2,3)
( 3,0) ( 3,1) ( 3,2) ( 3,3)

 

搜索第1行:

0,0) ( 0,1) ( 0,2) ( 0,3)
( 1,0)  × ( 1,1)  × ( 1,2)  × ( 1,3)
( 2,0) ( 2,1) ( 2,2) ( 2,3)
( 3,0) ( 3,1) ( 3,2) ( 3,3)

 

搜索第2行:

0,0) ( 0,1) ( 0,2) ( 0,3)
( 1,0)  × ( 1,1)  × ( 1,2)  × ( 1,3)
( 2,0) ( 2,1) ( 2,2) ( 2,3)
( 3,0) ( 3,1) ( 3,2) ( 3,3)

搜索第3行(最后一行)

0,0) ( 0,1) ( 0,2) ( 0,3)
( 1,0)  × ( 1,1)  × ( 1,2)  × ( 1,3)
( 2,0) ( 2,1) ( 2,2) ( 2,3)
( 3,0) × ( 3,1) × ( 3,2)  sum++ ( 3,3)

然后接着从(0,2)搜索起,相信这么一个图解过程,应该能看懂了吧。下面贴出题目AC的完整代码:

#include <iostream>
#include <cmath>
using namespace std;
#define MAXN 16
int queen[MAXN];   //皇后位置的选取情况
int sum = 0;       //迭代结果变量
int result[MAXN] = {0}; //保存结果变量,防止测试量多的情况下发生超市
int m;             //题目输入变量,皇后数(棋盘边长)
bool check( int n ){
    for( int i = 0; i < n; i++ ){
        //检查在第n行之前是否有在同一列或者45度对角线上
        if( queen[i] == queen[n] || abs( queen[i] - queen[n]) == ( n - i )){
            return false;
        }
    }
    return true;
}
void put( int n ){
    //从第n行开始dfs,搜索第n行的m列
    for( int i = 0; i < m; i++ ){
        queen[n] = i;
        //检测成功
        if( check(n) ){
            //搜索到最后一行了
            if( n == m - 1 ){
                sum++;
            }
            else{
                put( n + 1);
            }
        }
    }
}
int main()
{
    while( cin>>m, m ){
        sum = 0;
        if( result[m] != 0 ){
            cout<<result[m]<<endl;
        }
        else{
            put(0);
            result[m] = sum;
            cout<<result[m]<<endl;
        }
    }
    return 0;
}

你可能感兴趣的:(HDU)