八皇后问题,是一个古老而著名的问题,是回溯算法的典型案例。该问题是国际西洋棋棋手马克斯·贝瑟尔于1848年提出:在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。
高斯认为有76种方案。1854年在柏林的象棋杂志上不同的作者发表了40种不同的解,后来有人用图论的方法解出92种结果。计算机发明后,有多种计算机语言可以解决此问题。
你当然可以写8个for循环,但这不是蓝桥杯。
思路是这样的,每一行只能放一个皇后,这是这个问题的特点(当然每一列每一条斜线上都只能放一个皇后,这里我们放在检查部分讨论)
首先说一下步骤:
第四步乍一看可能想不通,因为我没有做一张直观的图出来,咱们看代码。
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还是简单,直接上代码了。
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)
代码量显而易见,虽然打印函数略微简陋,但这不影响主体部分的简短。