算法学习笔记之三:八皇后问题(递归、回溯)

(一)题记

从去年下半年开始找工作,大大小小也被“鄙”试、“面”试了n多回了。说实话只怪自己并未对常见的笔试题、面试题进行准备,导致败下阵来。一门学问要想学透学精是需要时间的,慢慢来吧……

第一次听到“八皇后”问题是在参加百度计算机视觉算法工程师面试时听中科院来面试的一个博士说的,当时隐约记得他是搞机器学习、模式识别的,所以自己以为这是很难的一个问题,回来简单想了一下也就没有细究。到后来去本行业初创公司“春雨”面试的时候,面试官让当场码代码解决这个问题,由于之前有印象,就定式思维地一直以为很难,所以从开始内心有些许的恐惧,最后在面试官的提示下写了个大概,最终结果不是很令人满意。

废话不多说了,一句话“不要被思维定式所困扰“,其实很简单的一个问题。

下面直奔主题:

(二)问题

http://zh.wikipedia.org/wiki/八皇后问题

八皇后问题是一个以国际象棋为背景的问题:如何能够在 8×8 的国际象棋棋盘上放置八个皇后,使得任何一个皇后都无法直接吃掉其他的皇后?为了达到此目的,任两个皇后都不能处于同一条横行、纵行或斜线上。八皇后问题可以推广为更一般的n皇后摆放问题:这时棋盘的大小变为n×n,而皇后个数也变成n当且仅当 n = 1 或 n ≥ 4 时问题有解。

算法学习笔记之三:八皇后问题(递归、回溯)_第1张图片

(三)思路分析

其实该问题并不难,利用递归方法很容易解决。没放置一个皇后,就将其能够攻击的区域进行标记,然后放置下一个皇后,一次类推……;此外,如果有解最终肯定是每一行有且只有一位皇后,所以放置的时候按照逐行放置的顺序进行。此问题难点在于如何把控递归函数的返回条件,一种条件是8个皇后放置完成后,返回成功,一种条件是该行中已经没有可以放置的位置,此时返回失败,需要重新放置。此时要额外注意,所谓的“重新放置”指的并不是将所有皇后清除重新来过,而是只返回上一层,将上一个导致本次放置失败的皇后进行清除,然后重新更新其位置,通过逐级放置、或逐级回溯可以达到遍历所有情况找到所有解(下文中给出的自己的代码的计算结果不是独立解的个数,而是所有可行解的情况)

(四)自己码代码

//--------------------------------------
//利用函数递归,解决八皇后问题
//
//	zssure	2014-03-12
//--------------------------------------

#include <stdio.h>
#include <cmath>

int count=0;//全局计数变量

/*--------------------四个皇后----------------------*/
//#define QUEEN_NUM 4
//int label[QUEEN_NUM][QUEEN_NUM]={	0,0,0,0,	
//									0,0,0,0,
//									0,0,0,0,
//									0,0,0,0	};

/*--------------------五个皇后----------------------*/
//#define QUEEN_NUM 5
//int label[QUEEN_NUM][QUEEN_NUM]={	0,0,0,0,0,
//									0,0,0,0,0,
//									0,0,0,0,0,
//									0,0,0,0,0,
//									0,0,0,0,0		};

/*--------------------六个皇后----------------------*/
//#define QUEEN_NUM 6
//int label[QUEEN_NUM][QUEEN_NUM]={	0,0,0,0,0,0,
//									0,0,0,0,0,0,
//									0,0,0,0,0,0,
//									0,0,0,0,0,0,
//									0,0,0,0,0,0,
//									0,0,0,0,0,0
//													};

/*--------------------七个皇后----------------------*/
//#define QUEEN_NUM 7
//int label[QUEEN_NUM][QUEEN_NUM]={	0,0,0,0,0,0,0,
//									0,0,0,0,0,0,0,
//									0,0,0,0,0,0,0,
//									0,0,0,0,0,0,0,
//									0,0,0,0,0,0,0,
//									0,0,0,0,0,0,0,
//									0,0,0,0,0,0,0
//													};

/*--------------------八个皇后----------------------*/
#define QUEEN_NUM 8
int label[QUEEN_NUM][QUEEN_NUM]={0,0,0,0,
								 0,0,0,0,
								 0,0,0,0,
								 0,0,0,0,
								 0,0,0,0,
								 0,0,0,0,
								 0,0,0,0,
								 0,0,0,0,
								 0,0,0,0,
								 0,0,0,0,
								 0,0,0,0,
								 0,0,0,0,
								 0,0,0,0,
								 0,0,0,0,
								 0,0,0,0,
								 0,0,0,0};


void FillChessbox(int m,int n,int num)
{
	for(int i=0;i<QUEEN_NUM;++i)
		for(int j=0;j<QUEEN_NUM;++j)
			if(abs(i-m)==abs(j-n))//对角区域填充
			{
				if(label[i][j]==0)
					label[i][j]=num;
			}

	int i=0,j=0;
	while(i<QUEEN_NUM)//行填充
	{
		if(label[i][n]==0)
			label[i][n]=num;
		++i;
	}
	while(j<QUEEN_NUM)//列填充
	{
		if(label[m][j]==0)	
			label[m][j]=num;
		++j;
	}

}
void ClearChessBox(int m,int n,int num)
{
	for(int i=0;i<QUEEN_NUM;++i)
		for(int j=0;j<QUEEN_NUM;++j)
			if(abs(i-m)==abs(j-n) && label[i][j]==num)
				label[i][j]=0;
	int i=0,j=0;
	while(i<QUEEN_NUM)
	{
		if(label[i][n]==num)
			label[i][n]=0;
		++i;
	}
	while(j<QUEEN_NUM)
	{
		if(label[m][j]==num)
			label[m][j]=0;
		++j;
	}
}
void AllClear()
{
	for(int i=0;i<QUEEN_NUM;++i)
		for(int j=0;j<QUEEN_NUM;++j)
			label[i][j]=0;

}
void PrintResult()
{
	for(int i=0;i<QUEEN_NUM;++i)
	{
		for(int j=0;j<QUEEN_NUM;++j)
			printf("%d ",label[i][j]);
		printf("\n");
				
	}
}
bool EightQueen(int n/*皇后个数*/,int c/*已经放置的皇后个数*/)
{
	//static int count=0;
	//小于3x3的棋盘是无解的
	if(n<4)
		return false;

	for(int i=0;i<n;++i)
	{
		if(label[c-1][i]==0)//存在可以放置第c个皇后的位置
		{
			label[c-1][i]=c+1;
			if(c==n)/*已经放置完毕所有的皇后*/
			{
				++count;
				PrintResult();
				printf("**************************\n");
				ClearChessBox(c-1,i,c+1);
				//AllClear();
				return true;
			}
			FillChessbox(c-1,i,c+1);
			EightQueen(n,c+1);
			ClearChessBox(c-1,i,c+1);
			/*-------------------------------------------------------------------------------------------------------------------------
			//	现场恢复,无论下一个皇后是否顺利放置,都应该恢复现场。原因是
			//
			//	如果下一个皇后放置失败,那么自然应该将本次放置的皇后去除,重新放置,所以需要进行现场恢复(即回溯);
			//	如果下一个皇后放置成功,意味着本次放置已经满足条件,是一个解,此时需要恢复现场,进行下一次的重新放置,寻找下一个解。
			//
			//------------------------------------------------------------------------------------------------------------------------*/
			//if(!EightQueen(n,c+1))
			//	ClearChessBox(c-1,i,c+1);

		}
	}
	return false;
}

int main()
{
	EightQueen(QUEEN_NUM,1);
	printf("%d\n",count);
	return 0;
}
【注】:本次的代码使用了二维数组来标记皇后的位置,且最后输出结果中皇后位置并未准确显示,还有待改进。
参考博文:

http://blog.csdn.net/feixiaoxing/article/details/6877965

http://blog.csdn.net/sandyzhs/article/details/4250563


Author:zssure

E-mail:[email protected]

Date:2014-03-12

你可能感兴趣的:(算法学习,码代码)