八皇后问题
【介绍】八皇后问题,是一个古老而著名的问题,是回溯算法的典型案例。该问题是国际西洋棋棋手马克斯·贝瑟尔于1848年提出:在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。

回溯求解
回溯:即递归枚举算法,特点如其名,当无解时函数将返回上一层的调用。
解决问题的重点就是递归枚举,以行作为参数依次向下进行遍历。对于每一行,我们枚举将皇后放在不同列时的情况,那么就需要依次判断前n-1行是否有皇后的位置与我们假设的位置相冲突。
通过分析不难发现,要想满足八皇后情况,那么所有行和所有列上均不能同时有两个皇后,也就是:每行每列均应有且只有一个元素 。
引进数组 a[8] 来记录每一列的情况,剩下的步骤就是判断这一位置的摆放是否满足要求。两个在对角线上的元素应该满足如下性质:
- 主对角线上:C1-R1=C2-R2
- 副对角线上:C1+R1=C2+R2
根据以上性质进行枚举判断即可,代码如下:
【代码】
void Eight_Queens(int cur)
{
if (cur == n)
tot++;
else
for (int i = 0;i < n;i++) //对于cur行来说,考虑是否有能放的位置
{
int flag = 1;
a[cur] = i; //尝试把第cur行的皇后放在第i列
for(int j=0;j<cur;j++)
if (a[cur] == a[j] || a[cur] + cur == a[j] + j || cur - a[cur] == j - a[j])
{
flag = 0;break;
}
if (flag)
Eight_Queens(cur + 1);
}
}
提高程序效率
之前我们通过依次判断前几行是否符合要求的方式来求解,需要两层循环:外层循环假设将皇后放在第i列,内存循环用来判断前cur-1行是否满足。但是有个更好地方法:
使用二维数组 vist[3][] 来直接判断当前尝试的皇后所在的列和两个对角线是否已有其他皇后。注意到主对角线标识y-x可能为负,存取时要加上n。
若在回溯法中使用了辅助的全局变量,则一定要及时把他们恢复原状。特别的,如果函数有多个出口,则需在每个出口处恢复被修改的值。
【代码】
void Eight_Queens(int cur)
{
if (cur == n)
tot++;
else
for (int i = 0;i < n;i++) //对于cur行来说,考虑是否有能放的位置
{
if (!vist[0][i] && !vist[1][cur + i] && !vist[2][cur - i + n])
{
a[cur] = i;
vist[0][i] = vist[1][cur + i] = vist[2][cur - i + n] = 1;
Eight_Queens(cur + 1);
vist[0][i] = vist[1][cur + i] = vist[2][cur - i + n] = 0;
}
}
}
八皇后问题与回溯算法

探讨了八皇后问题的经典解决方案,介绍了如何使用回溯算法高效地找出所有可能的摆放方式,避免皇后互相攻击。
1万+

被折叠的 条评论
为什么被折叠?



