利用回溯的八皇后问题

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

利用回溯的八皇后问题_第1张图片

 

利用回溯法解决这个问题:

       回溯算法的基本思想是:从一条往前走,能进则进,不能进则退回来,换一条路再试。

       书面一点:从问题的某一种状态出发,搜索可以到达的所有状态;

                        当某个状态到达后(即满足了要求) 或者该状态不符合要求,可向前回退,并继续搜索其他可达到的状态;

                        当所有状态都到达后,回溯算法结束。

 

    算法解决思路:

       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;
}


 

 

你可能感兴趣的:(利用回溯的八皇后问题)