一、八皇后问题
八皇后问题,是一个古老而著名的问题,是回溯算法的典型案例。该问题是国际西洋棋棋手马克斯·贝瑟尔于1848年提出:在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。 高斯认为有76种方案。1854年在柏林的象棋杂志上不同的作者发表了40种不同的解,后来有人用图论的方法解出92种结果。
二、算法思路:
首先我们分析一下问题的解,我们每取出一个皇后,放入一行,共有八种不同的放法,然后再放第二个皇后,同样如果不考虑规则,还是有八种放法。
于是我们可以用一个八叉树来描述这个过程。从根节点开始,树每增加一层,便是多放一个皇后,直到第8层(根节点为0层),最后得到一个完全八叉树。
我们先对问题解的结构做一个约定:
用gEightQueen[i]来表示,在第i行,皇后放在了(i,gEightQueen[i])这个位置。
我们考虑条件:
一共有四个,不能同一行,不能同一列,不在同一左右对角线上。
同一行、同一列好解决,
左对角线是x坐标和y坐标的和不相等,右对角线是x-y不相等,有正负之分。
三、代码,主要用到了递归和回溯,注释非常清楚
#include<iostream>
using namespace std;
int num=0;
static int gEightQueen[8] = { 0 }, gCount = 0;//用gEightQueen[i]来表示,在第i行,皇后放在了gEightQueen[i]这个位置。
void print()//输出每一种情况下棋盘中皇后的摆放情况
{
cout<<"No."<<num<<endl;
for (int i = 0; i < 8; i++)
{
int inner;
for (inner = 0; inner < gEightQueen[i]; inner++)
{
cout << "0 ";
}
cout <<"# ";
for (inner = gEightQueen[i] + 1; inner < 8; inner++)
{
cout << "0 ";
}
cout << endl;
}
cout << "==========================\n";
}
int check_pos_valid(int loop, int value)//检查是否存在有多个皇后在同一行/列/对角线的情况,loop和value为要检查的位置
{//index和data是皇后的行和列 loop是检查位置的行,value是检查位置的列
int index;
int data;
for (index = 0; index < loop; index++)//检查index以上所有行的皇后,是否跟当前位置冲突
{
data = gEightQueen[index];//初始设为0,行列范围是1-8
if (value == data)
return 0;
if ((index + data) == (loop + value))
return 0;
if ((index - data) == (loop - value))
return 0;
}
return 1;
}
void eight_queen(int index)//index是当前检查行
{
int value;
for (value = 0; value < 8; value++)//检查index行上所有列
{
if (check_pos_valid(index, value))//如果index行上有不会导致冲突的列坐标,就放一个皇后
{
gEightQueen[index] = value;
if (7 == index)//如果放到第8行,就计数、打印,函数结束 。
{//这里就是回溯,在每一个节点处向下把所有的可能做完了才向上返回
gCount++, num++,print();
//gEightQueen[index] = 0; 这两句是递归结束后,把数据清零
return;
}
eight_queen(index + 1);//没到第八行,就检查下一行
//gEightQueen[index] = 0; 递归下层返回时,把数据清零,这里删掉没关系是因为每次使用都是赋值覆盖后,没有预设它本来是什么值
}
}
}
int main(int argc, char*argv[])
{
eight_queen(0);
cout << "total=" << gCount << endl;
return 0;
}
四、运行结果