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

思路:

  1. 一维数组array存放皇后位置,array[i]=j 表示i行j列有一个皇后。
  2. 判断该位置放置皇后是否合法。
  3. 该位置合法,则进行下一行放置。
  4. 该位置非法则列值j++,进行第2步的判断。
  5. 当放满8个皇后(即i从0–>7)时,找到结果+1。
  6. 当某行中的列值(即array[i])>=8时,说明该行在前面行的摆放情况下无解。
  7. 则回退到上一行,对上一行的列值j++,进行2步判断。
  8. 当row回退到第一行(i=0),且第一行的列值超出范围(array[0]>=8)时,代表搜索结束。
  9. 输出所有可行解。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
#include <stdio.h>
#include <stdlib.h>

#define N 8

int check(int *q, int row)
{
if(row == 0)
{
return 1;
}
else
{
int y;
int x0 = q[row]; /* row行的列值 */
for(y = 0; y < row; y++)
{
int x = q[y]; /* 前row行每列的值 */
if(x == x0) /* 列值不能相同 */
return 0;
else if(x - x0 == row - y) /* 对角线方向1 */
return 0;
else if(x0 - x == row - y) /* 对角线方向2 */
return 0;
}
}
return 1;
}

int queen()
{
/****************************************
q[N]记录各行可行的摆放位置。下标是行坐标,
对应下标存储的值是可行解的列坐标。
*****************************************/
int q[N] = {0};
int found = 0; /* 记录可行解的个数 */
int row = 0; /* 行 */
int done = 0;

while(done == 0)
{
if(check(q, row))
{
if(row == N-1) /* 所有位置都可行后,得到一个解 */
{
found++;
}
else
{
row++; /* 新一行 */
q[row] = 0;
continue;
}
}
q[row] += 1; /* 列值+1 */
while(q[row] >= N) /* 当前行的列超出范围时 */
{
row--; /* 上一行 */
if(row >= 0)
{
q[row]++; /* 对应行的列值+1 */
}
else /* row < 0寻找结束 */
{
done = 1;
break;
}
}
}
return found;
}

int main()
{
int found;
found = queen();
printf("found = %d\n",found);
return 0;
}

递归版本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
int search(int *array, int row)
{
int count = 0;
int i;
for(i = 0; i < N; i++)
{
array[row] = i;
if(check(array, row))
{
if(row == N - 1)
{
count++;
}
else
{
count += search(array, row + 1);
}
}
}
return count;
}

int queen()
{
int array[N] = {0};
return search(array, 0);
}