您的位置:首页 > 其它

利用回溯的八皇后问题

2013-11-17 11:38 225 查看
       八皇后问题,是一个古老而著名的问题,是回溯算法的典型例题。该问题是国际西洋棋棋手马克斯·贝瑟尔于1848年提出:在8X8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。
高斯认为有76种方案。1854年在柏林的象棋杂志上不同的作者发表了40种不同的解,后来有人用图论的方法解出92种结果。计算机发明后,有多种方法可以解决此问题。



[align=left] [/align]

利用回溯法解决这个问题:
       回溯算法的基本思想是:从一条往前走,能进则进,不能进则退回来,换一条路再试。
       书面一点:从问题的某一种状态出发,搜索可以到达的所有状态;
                        当某个状态到达后(即满足了要求) 或者该状态不符合要求,可向前回退,并继续搜索其他可达到的状态;
                        当所有状态都到达后,回溯算法结束。
 
    算法解决思路:
       1.初始化: i = 1;  //行
       2.初始化: j = 1;  //列
       3. 从第i行开始,恢复j的当前值,判断第j个位置:
           a、 位置j可放入皇后:标记位置(i,j),i++, 执行步骤2
           b、 位置j不可放入皇后:j++,执行步骤a;
           c、 当j>8时,i--(回溯),执行步骤3。
       4. 结束: 当第8行可以放入皇后。
程序代码:
#include <stdio.h>

#define N 8

typedef struct Pos  //记录偏移量
{
int i;
int j;
}Pos;

static char board[N+2][N+2];  //增加边界,方便递归使用
static Pos pos[] = {{-1, -1}, {-1, 0}, {-1, 1}}; //正对角线上,每一列上, 斜对角线上的偏移量
static int count = 0;  //统计多少中放法;

/**
* 初始化数组,边界上的值用'#'表示, 其余数值用' '表示
**/
void init()
{
int i = 0;  //行
int j = 0;  //列

for(i=0; i < N+2; i++)
{
board[0][i]   = '#';
board[N+1][i] = '#';
board[i][0]   = '#';
board[i][N+1] = '#';
}

for(i=1; i<=N; i++)
{
for(j=1; j<=N; j++)
{
board[i][j] = ' ';
}
}
}

/**
* 显示摆放结果
**/
void display()
{
int i = 0;
int j = 0;

for(i=0; i<N+2; i++)
{
for(j=0; j<N+2; j++)
{
printf("%c", board[i][j]);
}
printf("\n");
}
}

/**
* 检测第i行j列上摆放一个女皇是否符合要求
**/
int check(int i, int j)
{
int ret = 1; //返回值
int p = 0;

for(p=0; p<3; p++)  //开始在正对角线,列,斜对角线上分别进行检测
{
int ni = i;
int nj = j;

while(ret && (board[ni][nj] != '#'))
{
ni = ni + pos[p].i; //坐标偏移量
nj = nj + pos[p].j;

ret = ret && (board[ni][nj] != 'K');
}
}

return ret;
}

/**
*查找第i行摆放女皇的情况
**/
void find(int i)
{
int j = 0;

//如果i已经超出第8行,则表示摆放已经完毕,直接显示摆放结果
if(i > N)
{
count++;
printf("total: %d\n", count);

display();

//getchar();
}
else //还没有摆放结束
{
for(j=1; j<=N; j++)
{
//检测(i,j)位置处是否可以摆放
if(check(i, j))
{
board[i][j] = 'K';

find(i+1); //继续查找下一行的摆放情况

//find(i+1)没有找到正确的摆放状态,回溯到前一状态,然后继续检测(i, j+1)处的摆放是否合法
// 或者find(i+1)正确找到了摆放状态,并且已经打印出摆放结果,下面要继续查找另一种摆放方法(i, j+1)是否可行,所以也要回溯到上一状态
board[i][j] = ' ';
}
}
}
}

int main()
{
init();
find(1);

return 0;
}


 
 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: