回溯法,八皇后问题

在国际象棋中,皇后是最强大的一枚棋子,可以吃掉与其在同一行、列和斜线的敌方棋子。比中国象棋里的车强几百倍,比她那没用的老公更是强的飞起(国王只能前后左右斜线走一格)。上图右边高大的棋子即为皇后。

   八皇后问题是这样一个问题:将八个皇后摆在一张8*8的国际象棋棋盘上,使每个皇后都无法吃掉别的皇后,一共有多少种摆法?此问题在1848年由棋手马克斯·贝瑟尔提出,岂止是有年头,简直就是有年头,82年的拉菲分分钟被秒的渣都不剩。

  八皇后问题是典型的回溯法解决的问题,我们以这个问题为例介绍回溯法。

  所谓回溯法,名字高大上,思想很朴素。设想把你放在一个迷宫里,想要走出迷宫,最直接的办法是什么呢?没错,试。先选一条路走起,走不通就往回退尝试别的路,走不通继续往回退,直到找到出口或所有路都试过走不出去为止。

《回溯法,八皇后问题》这里稍微用数学分析一下,用i,j表示当前正在检测的两列(i外层for循环,j内层for循环),那么a[i] ,a[j] 的值就分别表示当前检测列棋子摆放的位置即行(每列只有1个棋子)。

如果两个棋子对角线冲突(正反对角线冲突),则必然有:

abs(a[i]-a[j]) == i-j

优化整理后的冲突判断代码就出炉了,如下所示:

//位置冲突算法 
bool Chongtu(int a[], int n)//a[]位置数组,n皇后个数 
{
    for (int i = 2; i <= n; ++i)//i:位置 
        for (int j = 1; j <= i-1; ++j)//j:位置 
            if ((a[i] == a[j]) || (abs(a[i]-a[j]) == i-j))//1:在一行;2:在对角线上 
                return false;   //冲突 
    return true;//不冲突 
}

关于内层for循环j<=i-1,因为判断第i个棋子是否冲突(摆放是否合理),我们只需要和前面i-1列校对就ok了。这样也保证了,i>j的恒成立。所以对角线冲突了简化了一下。

好了,该说明的都说明了,现在编写第一个八皇后代码~~~~

枚举法:

思想:八重枚举,枚举出所以摆放的情况(不管合理不合理),然后到第八层for里面判断当前枚举出来的情况是否合理~~~~

#include <stdio.h>
#include <math.h>
//位置冲突算法 
bool Chongtu(int a[], int n)//a[]位置数组,n皇后个数 
{
    int i = 0, j = 0;
 
    for (i = 2; i <= n; ++i)//i:位置 
        for (j = 1; j <= i-1; ++j)//j:位置 
            if ((a[i] == a[j]) || (abs(a[i]-a[j]) == i-j))//1:在一行;2:在对角线上 
                return false;   //冲突 
    return true;//不冲突 
}
//八皇后:枚举算法 
void Queens8()
{
    int a[9] = {0}; //用于记录皇后位置:(第0行0列我们不用)。如:a[3] = 4;表示第3列第4行位置有皇后
    int i = 0,count = 0;  //用于计数 
 
    for (a[1] = 1; a[1] <= 8; ++a[1])
        for (a[2] = 1; a[2] <= 8; ++a[2])
            for (a[3] = 1; a[3] <= 8; ++a[3])
                for (a[4] = 1; a[4] <= 8; ++a[4])
                    for (a[5] = 1; a[5] <= 8; ++a[5])
                        for (a[6] = 1; a[6] <= 8; ++a[6])
                            for (a[7] = 1; a[7] <= 8; ++a[7])
                                for (a[8] = 1; a[8] <= 8; ++a[8])
                                {
                                    if (!Chongtu(a,8))//如果冲突,则继续枚举 
                                        continue;
                                    else
                                    {
                                        printf("第%d情况:",++count);
                                        for (i = 1; i <= 8; ++i)
                                            printf("%d ",a[i]);//打印某种情况 
                                        printf("\n");
                                    }
                                }
}
//主函数 
int main()
{
    Queens8();
 
    return 0;
}

回溯法

#include <stdio.h>
#include <math.h>
 
int a[9] = {0};
int n = 8, count = 0;
 
//位置冲突算法 
bool Chongtu(int a[], int n)//a[]位置数组,n皇后个数 
{
    int i = 0, j = 0;
 
    for (i = 2; i <= n; ++i)//i:位置 
        for (j = 1; j <= i-1; ++j)//j:位置 
            if ((a[i] == a[j]) || (abs(a[i]-a[j]) == i-j))//1:在一行;2:在对角线上 
                return false;   //冲突 
    return true;//不冲突 
}
 
//八皇后问题:回溯算法(递归版) 
void Queens8(int k) //参数k:递归摆放第k个皇后 
{
    int i = 0;
 
    if (k > n)      //k>n:即k>8表示最后一个皇后摆放完毕 
    {
        printf("第%d种情况:",++count);
        for (i = 1; i <= n; ++i)
            printf("%d ",a[i]);//打印情况 
        printf("\n");
    }
    else   //8个皇后未全部摆放完毕        
    {
        for (i = 1; i <= n; ++i)//摆放第k个皇后时(转下一行) 
        {       //依次从列顶端开始搜索,一直到列底端,直到找到合适位置,如果未找到,自动返回上层递归(回溯) 
            a[k] = i;               
            if (Chongtu(a,k))//不冲突 
                Queens8(k+1);//递归摆放下一个皇后
        }
    }
    return;
}
 
//主函数 
int main()
{
    Queens8(1);//参数1:表示摆放第1个皇后 
 
    return 0;
}


//  N皇后问题  
  
#include <iostream>  
using namespace std;  
  
#define N 8  
  
bool matrix[N + 1][N + 1] = {0};  
  
bool IsLegal(bool matrix[N + 1][N + 1], const int &i, const int &j)  
{  
    //  判断前面的i-1个棋子与matrix[i][j]是否冲突,i为1时合法  
  
    for (int m = 1; m <= i - 1; ++m) {  
        for (int n = 1; n <= N; ++n) {   //  实际每一行只有一个棋子  
            if (matrix[m][n] == 1) {  
                if ( n == j || abs(i - m) == abs(j - n) )   //  key, not bad  
                    return false;  
            }  
        }  
    }  
    return true;  
}  
  
void Print(bool matrix[N + 1][N + 1])  
{  
    static int count = 1;  
    printf("Case %d:\n", count++);  
    for (int i = 1; i <= N; i++) {  
        for (int j = 1; j <= N; j++) {  
            matrix[i][j] == 1 ? printf("%c ", 2) : printf(". ");  
        }  
        cout << endl;  
    }  
    cout << endl;  
}  
  
void Trial(const int i)  
{  
    //  进入本函数时,在N*N的棋盘前i-1行已放置了互不攻击的i-1个棋子  
    //  现从第i行起继续为后续棋子选择合适位置  
  
    if (i > N)   //  输出当前的合法布局  
        Print(matrix);  
    else  
        for (int j = 1; j <= N; ++j) {  
            matrix[i][j] = 1;  
            if ( IsLegal(matrix, i, j) )  
                Trial(i + 1);  
            matrix[i][j] = 0;  
        }  
}  
  
int main(void)  
{  
    Trial(1);  
  
    return 0;  
}  

?关于内层for循环j<=i-1,因为判断第i个棋子是否冲突(摆放是否合理),我们只需要和前面i-1列校对就ok了。这样也保证了,i>j的恒成立。所以对角线冲突了简化了一下

    原文作者:回溯法
    原文地址: https://blog.csdn.net/coolwriter/article/details/78987510
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞