回溯法解决八皇后问题

巧了我就是萌 提交于 2019-12-18 18:41:58

1.“八皇后”问题

八皇后问题是十九世纪著名数学家高斯于1850年提出的。问题是:在8*8的棋盘上摆放8个皇后,使其不能互相攻击,即任意的两个皇后不能处在同一行,同一列,或同一斜线上。可以把八皇后问题拓展为n皇后问题,即在n*n的棋盘上摆放n个皇后,使其任意两个皇后都不能处于同一行、同一列或同一斜线上。

2.冲突条件判断

如下图,方块内的值(i,j)表示第i行第j列。(1,1)位置如果放了皇后,那么(1,1)位置所在的行,列,斜线上都不能再放皇后。

如何判断两个位置冲突呢?

(1)判断两个位置是否再斜线上:根据斜率

A(1,1)位置,B(2,0)位置和C(2,2)位置,A和B冲突:(0-1)/(2-1)==-1,A和C冲突:(2-1)/(2-1)==1。

因此,对于任意两点A(x1,y1)和B(x2,y2),两点斜线冲突满足条件:abs((y2-y1)/(x2-x1))==1,

该公式转换为:abs(y2-y1)== abs(x2-x1)

(2)判断两个位置是否在同一列/同一行

任意两点A(x1,y1)和B(x2,y2)在同一列满足条件:y1 == y2,在同一行满足条件:x1 == x2


判断冲突代码如下:

int valid(int row, int col)    //判断第row行第col列是否可以放置皇后
{
    int i;
    for (i = 0; i < QUEEN; ++i)  //对已得到的解搜索,看是否冲突
    {   //判断列冲突与斜线上的冲突
        //表示第i行第col列放了皇后,(row - i) / (col - arr[i]) != 1 或 -1
        // arr[i] = k表示第i行第k列放了皇后
        if (a[i] == col || abs(i - row) == abs(a[i] - col))
            return 0;
    }
    return 1;
}

 

3.进入正题--回溯法解决

(1)初始化棋盘

#define INITIAL -10000 //棋盘的初始值const int QUEEN = 8;int a[QUEEN];//表示解空间a[1]=2表示[1,2]位置有皇后
void init()//初始化
{
    int *p;
    for(p=a;p<a+QUEEN;++p)
        *p = INITIAL;
}

(2)非递归回溯

void queen()      //N皇后程序
{
    int n = 0;
    int i = 0, j = 0;//i表示行,j表示每行的列
    while (i < QUEEN)//满足继续搜索的条件
    {
        ///没有到叶子结点情况下就继续搜索
        while (j < QUEEN)        //对i行的每一列进行探测,看是否可以放置皇后
        {
            if(valid(i, j))      //该位置可以放置皇后
            {
                a[i] = j;        //第i行放置皇后,位置位j列
                j = 0;           //第i行放置皇后以后,需要继续探测下一行的皇后位置,
                //所以此处将j清零,从下一行的第0列开始逐列探测
                break;
            }
            else
            {
                ++j;             //继续探测下一列
            }
        }
        ///搜索到了叶子结点但是无解,直接回溯到上一行
        if(a[i] == INITIAL)         //第i行没有找到可以放置皇后的位置
        {
            if (i == 0)             //回溯到第一行,仍然无法找到可以放置皇后的位置,
                //则说明已经找到所有的解,程序终止
                break;
            else                    //没有找到可以放置皇后的列,此时就应该回溯
            {
                --i;
                j = a[i] + 1;        //把上一行皇后的位置往后移一列
                a[i] = INITIAL;      //把上一行皇后的位置清除,重新探测
                continue;
            }
        }
        //找到解
        if (i == QUEEN - 1)          //最后一行找到了一个皇后位置,
            //说明找到一个结果,打印出来
        {
            printf("answer %d : \n", ++n);
            print();
            //不能在此处结束程序,因为我们要找的是N皇后问题的所有解,
            //此时应该清除该行的皇后,从当前放置皇后列数的下一列继续探测。
            j = a[i] + 1;             //从最后一行放置皇后列数的下一列继续探测
            a[i] = INITIAL;           //清除最后一行的皇后位置
            continue;
        }
        ++i;              //继续探测下一行的皇后位置
    }
}

(3)结果打印

void print()    //打印输出N皇后的一组解
{
    int i, j;
    for (i = 0; i < QUEEN; ++i)
    {
        for (j = 0; j < QUEEN; ++j)
        {
            if (a[i] != j)      //a[i]为初始值
                printf("%c ", '.');
            else                //a[i]表示在第i行的第a[i]列可以放置皇后
                printf("%c ", '#');
        }
        printf("\n");
    }
    for (i = 0; i < QUEEN; ++i)
        printf("%d ", a[i]);
    printf("\n");
    printf("--------------------------------\n");
}

 

 

 

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!