题目:N 皇后问题源自国际象棋,所有棋子中权力最大的称为皇后,它可以直着走、横着走、斜着走(沿 45 度角),可以攻击移动途中遇到的任何棋子。N 皇后问题的具体内容是:如何将 N 个皇后摆放在 N*N 的棋盘中,使它们无法相互攻击。
一、问题分析
该问题要求把n个皇后放在一个n×n的棋盘上,使任何两个皇后不能同行不能同列也不能位于同一对角线上。用回溯的思想对它求解:因为每个皇后都必须占据一行,所以我们的问题转化为给每行的皇后分配一个合适的列。
n=1,2,3时问题很简单,所以我们以n=4开始考虑,求解过程如下:
我们从空棋盘开始,首先将皇后1放到它所在行的第一个可能位置(此时为第一列),即皇后1的坐标为(1,1);对于皇后2,尝试将其填入第一列检查是否符合规则,发现失败,第二列也失败,第三列符合要求,即将皇后2填入(2,3);再将皇后3依规则找第一个可能的位置,但是发现四个位置都不符合,所以将算法回溯,把皇后2放在下一个可能位置,即(2,4)上,这样皇后3可以放在(3,2),去找皇后4能不能放,此时又发现没有符合的位置,然后回溯到皇后3,皇后3没有再可以尝试的位置,回溯到皇后2也没有再可以尝试的位置,回溯到皇后1,把皇后1放在(1,2),接着按相同步骤将皇后2放在(2,4),皇后3放在(3,1),皇后4放在(4,3),这就是n=4时的一个解,接着再次回溯找剩下的解。
二、算法思路
1、本题一共使用两个算法来进行解决,第一个是暴力搜索法,即将皇后所有可能的排列方式都排列出来并进行判断,算法中主要使用了一个数组和一个可以用来判断行列还有斜线上检测是否有棋子存在的判断方法来进行判断如果符合题目要求就保存下来,最后穷举完成后输出所有可能的排序。
2、第二种算法是回溯法。算法思路是从[0][0]号位置开始,如果在某一行找到了一个可以放置皇后的位置,记录该行的位置,并且更新相应的三个位置数组后进入下一行进行判断。如果在某行没有找到位置,那么意味着前面发生了错误,需要返回上一行进入该行的位置,将保存的上一行皇后的位置从三个位置数组中删除,接着判断上一行下一个未判断的空位。如果在最后一行找到了位置,那么意味着找到了一个解,接着保存该解。并且删除三个位置数组的位置,回溯至上一级,返回上一行对未判断的位置继续进行求解。直至对第一行所有位置扫描一遍后,找到所有解为止。
三、代码
1、暴力求解
#include<stdio.h>
#include<math.h>
#define n 5
int place(int a[n])
{
int i, j;
for(i = 0; i < n; i++)
for (j = i + 1; j < n; j++)
{
if ((a[i] == a[j]) || (abs(a[i] - a[j]) == abs(i - j)))//不同列和斜线
return 0;
}
return 1;
}
int main()
{
int a[n];
int num, temp;
int i;
int count = 0;
for (num = 0; num < pow(n, n); num++)
{
temp = num;
for (i = 0; i < n; i++)
{
a[i] = temp%n;
temp = temp / n;
}
if (place(a))
{
count++;
for (i = 0; i < n; i++)
{
printf("(%d,%d) ", i+1,a[i]+1);
}
putchar('\n');
}
}
printf("total is : %d\n", count);
return 0;
}
运行结果如下:
2、回溯法
def NQueens(n):
def checkBoard(rowIndex):
'''
# 检查盘面是否成立
:param rowIndex:
:return:
'''
for i in range(rowIndex): # rowIdex表示当前行号
if cols[i] == cols[rowIndex]: # 检查竖线(即列)
return False
if abs(cols[i] - cols[rowIndex]) == rowIndex - i: # 检查斜线
return False
return True
def helper(rowIndex):
if rowIndex == n: # 递归结束的边界条件
board = [[0 for _ in range(n)] for _ in range(n)]
for i in range(n):
board[i][cols[i]] = 1
res.append(board)
return
for i in range(n): # 依次尝试当前行的空格位置()
cols[rowIndex] = i
if checkBoard(rowIndex):
helper(rowIndex + 1) # 递归调用给子函数
cols = [False for _ in range(n)]
res = [] # 定义每行皇后的纵坐标
helper(0)
return res # 结束NQueens函数
if __name__ == '__main__':
li = NQueens(5) # 函数中的值,表示解决的8皇后问题
for i in range(len(li)):
print(f'{i+1}:{li[i]}')
运行结果: