八皇后问题 C&Py

八皇后

八皇后问题,是一个古老而著名的问题,是回溯算法的典型案例。该问题是国际西洋棋棋手马克斯·贝瑟尔于1848年提出:在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。
高斯认为有76种方案。1854年在柏林的象棋杂志上不同的作者发表了40种不同的解,后来有人用图论的方法解出92种结果。计算机发明后,有多种计算机语言可以解决此问题。

C语言解法

你当然可以写8个for循环,但这不是蓝桥杯。

思路是这样的,每一行只能放一个皇后,这是这个问题的特点(当然每一列每一条斜线上都只能放一个皇后,这里我们放在检查部分讨论)

首先说一下步骤:

  1. 在这一行随便找个位置放下去
  2. 检查这个位置能不能放
  3. 检查到能放了,进行下一行位置的判断(也就是递归到第一步)
  4. 回溯,找这一行还有没有另一个位置能放

第四步乍一看可能想不通,因为我没有做一张直观的图出来,咱们看代码。

int cnt = 0;
int table[8] = {-1,-1,-1,-1,-1,-1,-1,-1};

cnt计数八皇后问题有多少个解
table数组表示第几行的第几列已经放置了皇后,若该行还未放置皇后,则为-1

void queen(int y)
{
    int x = 0;//x表示要放置棋子的位置
    if(y > 7)//如果第y行是最后一行
    {
        show();//打印棋盘
        cnt++;//解法+1
    }
    else{
        for(x = 0; x < 8; x++)//从y行的0号位开始放置一个棋子
        {
            if(check(x,y))//如果这个位置允许放置
            {
                table[y] = x;//保存这个位置
                queen(y+1);//去下一行寻找合适的位置
                table[y] = -1;//回到这一行,将这个位置上的皇后移开,找该行还有没有能放皇后的位置
            }
        }
    }
}

为什么最后一行没有像普通判断那样归零呢
因为根本轮不到最后一行来和别的对比啊。
递归和回溯的逻辑到这里已经搞明白了,接下来就是check函数怎样写的问题了show函数在这里不做赘述。

两枚棋子是否在同意列上,这个好判断,直接检查table里面有没有值与当前的x相同即可;
如何检查斜线上呢,也简单,当|x1-x2| = |y1 - y2|时,这两个点在同一斜线上。

int check(int x,int y)
{
    int i = 0;//用i来循环判断行
    for(i = 0; i < y; i++)//从第0行到第y-1行判断
    {
        if(abs(x - table[i]) == abs(y - i))//如果当前行上的棋子与第y行的棋子冲突
            return 0;//返回false
        else if(x == table[i])//如果当前行上的棋子与第y行的棋子冲突
            return 0;//返回false
    }
    return 1;//没有检测到冲突,代表第y行的第x位置处可以放置棋子,返回true
}

好的,C语言的核心部分搞定,接下来上完整代码

#include 
#include 

int cnt = 0;
int table[8] = {-1,-1,-1,-1,-1,-1,-1,-1};

void show();
int check(int x,int y);
void queen(int y);

int main()
{
    queen(0);
    printf("共搜索到%d种解",cnt);
    return 0;
}

void show()
{
    int i,j;
    printf(" ________________\n");
    for(i = 0; i < 8; i++)
    {
        printf("|");
        for(j = 0; j < 8; j++)
        {
            if(j == table[i])
                printf("%2c",'*');
            else
                printf("%s","·");
        }
        printf("|\n");
    }
    printf(" ----------------\n");
    printf("\n");
}

int check(int x,int y)
{
    int i = 0;
    for(i = 0; i < y; i++)
    {
        if(abs(x - table[i]) == abs(y - i))
            return 0;
        else if(x == table[i])
            return 0;
    }
    return 1;
}

void queen(int y)
{
    int x = 0;
    if(y > 7)
    {
        show();
        cnt++;
    }
    else{
        for(x = 0; x < 8; x++)
        {
            if(check(x,y))
            {
                table[y] = x;
                queen(y+1);
                table[y] = -1;
            }
        }
    }
}

PYの解法

要么说py还是简单,直接上代码了。

cnt = 0#计数八皇后问题有多少个解
def queen(A, cur=0):#传入各行皇后位置以及,当前需要放置皇后的行数
    global cnt
    if cur == len(A):#如果当前行等于所需行数+1
        print(A)#打印皇后位置数据
        cnt+=1#解法+1
        return 0#返回上一层递归
    for col in range(len(A)):#col表示列
        A[cur], flag = col, True#cur是传入的目标行,flag作为标记,先假设这个位置(col)可以放皇后
        for row in range(cur):#row相当于是检查是否冲突,从第0行到检测到第cur-1行
            if A[row] == col or abs(col - A[row]) == cur - row:#冲突
                flag = False#flag表明无法放置
                break#去判断该行的下一列
        if flag:#如果无冲突
            queen(A, cur+1)#去判断该行的下一行
queen([None]*8)#调用该函数8表示8个皇后 None表示该行无皇后
print(cnt)

代码量显而易见,虽然打印函数略微简陋,但这不影响主体部分的简短。

你可能感兴趣的:(py,学习笔记)