八
皇后
问题,是一个古老而著名的问题,是
回溯算法
的典型案例。该问题是国际西洋棋棋手马克斯·贝瑟尔于1848年提出:在8X8格的
国际象棋
上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。百度百科解释
一般看到这种图,大家第一感觉肯定是用一个二维数组来表示出坐标信息,但此处,感觉题目进行分析,8X8的棋盘上摆放8个皇后,且不能同行、同列和同一斜线上,那么也就是说每行最多一个皇后。因此我们仅用一个长度为8的数组就可以表示出皇后的位置信息,该数组的索引N即为第N行,索引对应的值即为第N行皇后所处的列数。
另外,对于该问题,也不能简单的进行遍历,因为时间复杂度为8*8*8*8*8*8*8*8*8,8的8次方。换个角度:
1,在第N行放置皇后,只需要判断前N-1行皇后的位置与第N行皇后的关系,那么剩下的8-N行在这个循环中是不需要判断的(因为此时剩下的行还没有皇后啊^_^)。如果第N行放置成功,则在此基础上尝试放置第N+1行的皇后;
2,如果第N行放置失败,则要回溯到第N-1行,重新选择第N-1行皇后所处的列数。如果第N-1行依然放置失败,则再回溯到第N-2行,以此类推;
3,如果第N行为最后一行了,则说明完全成功,打印整个棋盘。
#include <stdio.h>
#include <malloc.h>
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <stdlib.h>
// 打印整个阵列
void printPos(const int *pos, const int cnt)
{
#define QUEEN '#'
#define NOT_QUEEN '*'
// 记录共有多少组满足条件的组合。
static int total = 0;
printf("case %d:\n", ++total);
for(int i = 0; i < cnt; ++i){
for(int j = 0; j < cnt; ++j){
if(pos[i] != j){
printf("%c ", NOT_QUEEN);
}else{
printf("%c ", QUEEN);
}
}
printf("\n");
}
printf("\n");
}
// 尝试在第row行插入的第col列插入值
int tryInsertPos(int* pos, const int cnt, int row, int col)
{
for(int i = 0; i < row; ++i){
int otherRow = i; // row行之上的皇后所处的行数。
int otherCol = pos[otherRow]; // row行之上的皇后所处的lie数。
// 不能在同一列。
if(col == pos[otherRow]){
return -1;
}
// 不能在对角线上。
if(abs(col - otherCol) == abs(row - otherRow)){
return -1;
}
}
pos[row] = col;
// 最后一行,已经满足条件了。
if(cnt == row + 1){
printPos(pos, cnt);
return 0;
}else{
int ret = 0;
// 尝试在下一行的各个位置放置皇后。
for(int i = 0; i < cnt; ++i){
ret = tryInsertPos(pos, cnt, row + 1, i);
if(ret != 0){
continue;
}
}
return ret;
}
}
int main(int argc, char** argv)
{
int CNT = 8;
if(argc == 2){
CNT = atoi(argv[1]);
}
// 数组的索引为皇后所在的行数,索引对应的值表示该皇后对应的列数。
// 如pos[1] = 2,则表示在第二行第三列存在皇后。
int *pos = (int*)malloc(sizeof(int) * CNT);
if(NULL == pos){
printf("line %d malloc error\n", __LINE__);
return -1;
}
memset(pos, 0, sizeof(int) * CNT);
//tryInsertPos(pos, CNT, 0, 0);
for(int i = 0; i < CNT; ++i){
tryInsertPos(pos, CNT, 0, i);
}
free(pos);
return 0;
}
版权声明:本文为博主原创文章,未经博主允许不得转载。