N皇后问题是一个经典的问题,在一个N*N的棋盘上放置N个皇后,每行一个并使其不能互相攻击(同一行、同一列、同一斜线上的皇后都会自动攻击)
回溯算法也叫试探法,它是一种系统地搜索问题的解的方法。回溯算法的基本思想是:从一条路往前走,能进则进,不能进则退回来,换一条路再试。首先就是在棋盘上如何判断两个皇后是否能够相互攻击,在最初接触这个问题时,首先想到的方法就是把棋盘存储为一个二维数组,然后在需要在第i行第j列放置皇后时,根据问题的描述,首先判断是在第i行是否有皇后,由于每行只有一个皇后,这个判断也可以省略,然后判断第j列是否有皇后,这个也很简单,最后需要判断在同一斜线上是否有皇后,按照该方法需要判断两次,正对角线方向和负对角线方向,总体来说也不难。但是写完之后,总感觉很笨,因为在N皇后问题中这个函数的使用次数太多了,而这样做效率较差。上网查看了别人的实现之后大吃一惊,使用一个一维数组来存储棋盘,在某个位置上是否有皇后可以相互攻击的判断也很简单。
把棋盘存储为一个N维数组a[N],数组中第i个元素的值代表第i行的皇后位置,这样便可以把问题的空间规模压缩为一维O(N),在判断是否冲突时也很简单,首先每行只有一个皇后,且在数组中只占据一个元素的位置,行冲突就不存在了,其次是列冲突,判断一下是否有a[i]与当前要放置皇后的列j相等即可。至于斜线冲突,通过观察可以发现所有在斜线上冲突的皇后的位置都有规律即它们所在的行列互减的绝对值相等,即| row – i | = | col – a[i] |(之前自己做过的代码太丑了,所以贴个别人的代码。)
#include <iostream>
using namespace std;
#define NUM 8
static int gEightQueen[8] = {0};
static int gCount = 0;
void print()
{
int outer;
int inner;
for (outer = 0; outer < NUM; outer++)
{
for(inner = 0; inner < gEightQueen[outer]; inner++)
printf("*");
printf("#");
for(inner = gEightQueen[outer] +1; inner < NUM; inner++)
printf("*");
printf("\n");
}
printf("=====================================\n");
}
int checkPosValid(int loop, int value)
{
int index;
int data;
for(index = 0; index < loop; index++)
{
data = gEightQueen[index];
if(value == data)
return 0;
if((index + data) == (loop + value))
return 0;
if((index - data) == (loop - value))
return 0;
}
return 1;
}
void eightQueen(int index)
{
int loop;
for(loop = 0; loop < NUM; loop++)
{
if(checkPosValid(index,loop))
{
gEightQueen[index] = loop;
if(7 == index)
{
gCount++;
print();
gEightQueen[index] = 0;
return;
}
eightQueen(index + 1);
gEightQueen[index] = 0;
}
}
}
int main()
{
eightQueen(0);
printf("total = %d\n", gCount);
return 0;
}