经典搜索问题——易懂的八皇后~2020.7.1学习笔记

    技术2022-07-10  160

    输入格式

    6

    输出格式

    2 4 6 1 3 5 3 6 2 5 1 4 4 1 5 2 6 3 4

    此处借洛谷的题目描述说明问题。题目描述自然不必多说,上面已经说的很明白了,下面直接上代码。

    #include <iostream> using namespace std; int a[50]={0},b[50]={0},c[50]={0},d[50]={0};//记录八个方向上是否已经存在皇后 //此处八个方向指东西南北(上下左右)东北东南西北西南 //a数组指的是上下(此处用于存放第i行的可行解),b左右,c西北与东南,d东北与西南 int total=0,n;//可行的方案数 void print()//用于打印输出的函数,在满足搜索退出条件时执行。 { if(total<=2){//仅输出前三个解,以及解的总数。 for(int i=1;i<=n;i++) cout<<a[i]<<" "; cout<<endl; } total++; } void queen(int i) { if(i>n){//n为棋盘的长度,i>n即已经找到了足够的皇后放置位置,可以退出函数了。 print(); return; } else{//以下时算法的核心,也是最难理解的部分 for(int j=1;j<=n;j++){/*搜索函数传入的实参是从第一行开始,逐行开始寻找,因此 不必考虑上下的问题,因为每行只找一个,不存在行内重复。*/ if(!b[j]&&!c[i+j]&&!d[i-j+n])/*!b[j]代表b[j]所处位置值为零,即此处没有放 过皇后。c与d同,此处d[i-j+n]的意思与i-j相同,但由于数组的下标显然不能为 负数,因此加上一个n,此处的加n意味着在判断d时所有的下标都加上了n,这样便 不影响判断了(即+n仅仅是为了防越界,此外无实际意义,不影响判断)*/ { a[i]=j;//第i行的可行解位于第j列上 b[j]=1;//第j列上存在皇后,其他行上不可再在此列放置皇后 c[i+j]=1;//意同上 d[i-j+n]=1;//同上 queen(i+1);//去下一列搜索,即进入下一层 /*此处便是回溯的核心,上一行提到执行“queen(i+1);”进入 下一层,显然下一层不论结果如何(可能搜索失败了,也可能 成功了),都会从此处退出,接着执行下面的语句,因此,倘若 成功了,便撤销之前放置的皇后(方法是将b,c,d全部置0), 重新开始寻找。!为什么不将a也置零呢?因为这一部分的语句 存在于一个循环当中,无论a是否置零,在循环中都会被赋予一个新 值,置零显然是没有意义的,因为a的值一定会改变。!*/ b[j]=0; c[i+j]=0; d[i-j+n]=0; } } } } int main() { cin>>n; queen(1);//自第一层开始搜索 cout<<total; return 0; }

    搜索的核心是回溯,代码的重要内容已经在上述代码块中的注释阐明了,此处便不再赘述。同样,与递归如出一辙,熟练运用搜索也是建立在多多运用的基础上,熟能生巧。

    上学期我选了“程序设计专题训练”一门课,期末考试中的一道题我便运用了搜索的技巧成功解题,那时距离我初学搜索仅仅不到半个月的时间。我想说的是,搜素与递归这些算法(借用学长的话说,更确切的,它们不是算法,而更像一种思维<贪心也是>),其实并没有看起来的那么难,可能做五六道题(甚至不需要完全是你自己想出来的,借鉴题解<而不是抄题解>解题也未尝不可)便已经能对这种思维轻车熟路了。

    共同学习,共同进步。 码文不易,您不点个关注?

    Processed: 0.017, SQL: 9