N皇后问题摆法算法描述

题目说明:
在一个N×N的国际象棋棋盘中摆N个皇后,使这N个皇后不能互相被对方吃掉。

题目要求:
(1)依次输出各种成功的放置方法。
(2)最好能画出棋盘的图形形式,并动态的演示试探过程。
(3)程序能方便的移植到其它规格的棋盘上。
例如:在一个4×4的棋盘可以摆放的棋位置{(0,1)(1,3)(2,0)(3,2)},{(0,2)(1,0)(2,3)(3,1)}两种。

题目分析:
一、试探过程分析:
N×N皇后问题的求解过程就是一个试探回逆的过程。如图-1


(图1-1) 

1、首先查找第一行的可放位置,第一行全部可以放,那么我们就先将第一个皇后放在(0,0)点。

 
(图1-2)

2、再查找第二行,由于第一行的(0,0)已经放了皇后,故第二行的(1,0)和(1,1)都能放皇后了,可放的就是(1,2)和(1,3)点,在(1,2)放上皇后。

(图1-3)

3、再查找第三行,查找所以发现第三行没有可放的位置了,回逆到第二行讲皇后放到(1,3)再查找第3行。如果还不行,就回到第一行将第一行的皇后放人下一个可放的点,依次类推,查找N×N上的所以可放的位置,直到第一行所以位置被放完,输出结果。
4、根据上面的规律可以发现,对于一个皇后放在坐标(x,y),它的下一行位置为(x-1,y)(x,y)(x+1,y)的坐标都不能再放皇后。我们用一个数组来存放本行能放皇后的点。用循环来查找上面行对本行的影响,将收到影响的点置FAlSE。
5、计算公式为:
iPlaceOver[col - (column - i)] = false; 
iPlaceOver[col] = false;
iPlaceOver[col + (column - i)] = false;

其中col是上面行皇后的位置,column是当前的第N行。
6、跌代过程:

for (i = 0 ; i < m_iCount ; i ++)
{	
	if (iPlaceOver[i]) //如果是可以放皇后的位置
	{
		m_piSaveQPlace[column] = i;//保存位置 
		ComputQueenPlace(column + 1);//递归搜索下一行 
	} 
}
7、为了动态保存计算结果,程序使用了一个整形的数组指针存放每次结果中每行的位置。为了方便和清晰的显示,我使用了一个结构保存。
8、增加了一个位图保存函数,用来保存希望保存为位图的结果。

二、程序动态显示试探结果说明:
为了显示试探过程,把视图指针传为递归函数,用来在得到真确的结果的时候可以刷新视图显示结果。在显示的时候为了防止过分闪动,使用了内存DC将位图直接帖到视图中。

三、类结构规划:
class CQueen
{
private:
	struct PlaceList
    {
		int		*Place;
    };
	PlaceList * m_pPlaceList;
    int		m_iListMaxSize;
	int 	m_iListNowSize;
	int		m_iCount;
	CSize	m_sizeView;
	bool	m_bRuning;
	int		*m_piSaveQPlace; // 存每行中皇后的位置
	int		m_iNowCol;
	CBitmap *m_pGridBitmap;
	int		m_iDrawIndex;

public:
	void DrawQueenN(CDC *pDC);
	void DrawList(int index);
	void ComputQueenPlace(int column , CView *view = NULL); // 皇后问题求解函数 
	CSize GetQueenGridSize(); 
	int GetQueenPlace(int row); 
	int GetListSize(); 
	int GetDrawIndex(); 
	void SetRow(int row); 
	void SaveToBMPFile(); 
	CQueen(int row); 
	CQueen();
	~CQueen();

private:
	void DrawGird(CDC *pDC); 
	void DrawQueen(CDC *pDC); 
	void AddPlace(int *place); 
	void FreeList();
};
代码分析:
1、递归代码
void CQueen::ComputQueenPlace(int column , CView *view)  
{
	int row = 0;  
	int i ;  
	int col ;  

	m_iNowCol = column;

	if (column == m_iCount) // 相等说明全部递归完成  
	{
		AddPlace(m_piSaveQPlace);
		m_bRuning = false;  
		return;
	}

	m_bRuning = true;

	int *iPlaceOver = new int[m_iCount];

	for ( i = 0 ; i < m_iCount ; i ++)// 初始化为都能放棋子  
	{
		iPlaceOver[i] = true;  
	}

// 将不能放棋子的点置False  
	for (i = 0 ; i < column ; i ++)  
	{
		col = m_piSaveQPlace[i];  
		if ((col - (column - i)) >= 0)  
		{
			iPlaceOver[col - (column - i)] = false;  
		}
		
		if ((col + (column - i)) < m_iCount)  
		{
			iPlaceOver[col + (column - i)] = false;  
		}
		iPlaceOver[col] = false;
  	}

// 递归调用每一次的可能  
	for (i = 0 ; i < m_iCount ; i ++)  
	{
		if (iPlaceOver[i])  
		{
			m_piSaveQPlace[column] = i;  
			if (view != NULL && m_iDrawIndex == -1)  
			{
				CDC *pDC = view->GetDC();  
				DrawQueenN(pDC);
				view->ReleaseDC(pDC);
				Sleep(20);
			}
			ComputQueenPlace(column + 1 , view);  
		}
	}
	m_bRuning = false;  
	delete[] iPlaceOver;  
	m_iNowCol = 0;  
}
2、保存找到的点代码
void CQueen::AddPlace(int *place)
  {
	if (m_iListNowSize == m_iListMaxSize)   
	{
		m_iListMaxSize += 10;   
		PlaceList *temlist = new PlaceList[m_iListMaxSize];   
		for ( int i = 0 ; i < m_iListNowSize; i ++)   
		{
			temlist[i].Place = m_pPlaceList[i].Place;   
		}
		delete[] m_pPlaceList;   
		m_pPlaceList = temlist;
   	}
	int *iPlace = new int[m_iCount];
   
	for ( int i = 0 ; i < m_iCount ; i ++)   
	{
		iPlace[i] = place[i];   
	}
	m_pPlaceList[m_iListNowSize++].Place = iPlace;   
}

用户使用:

(图2-1)

程序运行结果:


(图3-1)

结束语

这段时间有些空,写点数据结构课程设计上面的题目。以后将陆续发点这方面的代码,供大家一起交流,如果代码中有什么不妥当或不构完美的地方,也请大家不吝赐教。

PHP实现代码:

<?php
/**
 * 八皇后问题
 *
 * 题目说明:
 * 在一个N×N的国际象棋棋盘中摆M个皇后,使这M个皇后不能互相被对方吃掉。
 * 题目要求:
 *(1)依次输出各种成功的放置方法。
 *(2)最好能画出棋盘的图形形式,并动态的演示试探过程。
 *(3)程序能方便的移植到其它规格的棋盘上。
 * 例如:在一个4×4的棋盘可以摆放的棋位置{(0,1)(1,3)(2,0)(3,2)},{(0,2)(1,0)(2,3)(3,1)}两种。
 */
/**
 * 函数:解决八皇后问题
 *
 * @param	array	二维数组
 * @return	array	三维数组, 注意:存在重复记录
 */
function eight_queen($arr) {
	$nr = count($arr);
	$nc = count($arr[0]);
	
	$ret_arr = array();
	
	for ($i = 0; $i < $nr; $i++) {
		for ($j = 0; $j < $nc; $j++) {
			//放置第一个皇后
			$tmp = kill_point($arr, $i, $j);
			//放置其他皇后
			for ($k = 0; $k < $nr; $k++) {
				for ($l = 0; $l < $nc; $l++) {
					if ($tmp[$k][$i] == 1 OR $tmp[$k][$l] == 2) {
						continue;
					} else {
						$tmp = kill_point($tmp, $k, $l);
					}
				}
			}
			$ret_arr[] = $tmp;
		}
	}
	
	return $ret_arr;	
}
 
/**
 * 函数: 输入指定位置的皇后,处理该位置皇后可能吃掉的位置点,修改其值为2(斜, 行, 列).
 *
 * @param	array	二维数组
 * @param	int		X轴坐标点
 * @param	int 	Y轴坐标点
 * @param	array	二维数组
 */
function kill_point($arr, $x = 0, $y = 0) {
	$n = count($arr);
	$a = $x; 
	$b = $y;
	
	//行
	for($i = 0; $i < $n; $i++) {
		$arr[$x][$i] = 2;
	}
	//列
	for($i = 0; $i < $n; $i++) {
		$arr[$i][$y] = 2;
	}
	//右下斜
	for (;$x < $n AND $y < $n;) {
		$arr[$x++][$y++] = 2;
	}
	//左下斜
	for ($x = $a, $y = $b; $x < $n AND $y >= 0;) {
		$arr[$x++][$y--] = 2;
	}
	//右上斜
	for ($x = $a, $y = $b; $x >= 0 AND $y < $n;) {
		$arr[$x--][$y++] = 2;
	}
	//左上斜
	for ($x = $a, $y = $b; $x >= 0 AND $y >= 0;) {
		$arr[$x--][$y--] = 2;
	}
	//本身位置处理为1
	$arr[$a][$b] = 1;
	
	return $arr;
}

/**
 * 函数: 去除数组重复记录
 *
 * @param	array	三维数组
 * @return	array	去重处理后的三维数组
 */
function myarr_unique($arr) {
	$tmp = array();
	
	foreach($arr as $val) {
		$tmp[] = serialize($val);
	}
	$tmp = array_unique($tmp);
	unset($arr);
	foreach($tmp as $val) {
		$arr[] = unserialize($val);
	}
	return $arr;
}

/**
 * 函数: 以表格形式输出皇后位置
 *
 * @param 	array	二维数组
 * @param	int		单元正方形的宽度值
 */
function display($arr, $w = 40) {
	$nr = count($arr);
	$nc = count($arr[0]);
	
	echo "<table border = '1' style = 'float:left; margin:10px 10px;' width = '".($w*$nr)."'>";
	for($i = 0; $i < $nr; $i++) {
		echo "<tr>";
		for($j = 0; $j < $nc; $j++) {
			if ($arr[$i][$j] == 1 ) {
				echo "<td width = '".$w."' height = '".$w."' align = 'center'>皇后</td>";
			} else if($arr[$i][$j] == 2) {
				echo "<td width = '".$w."' height = '".$w."'>&nbsp;</td>";
			} else {
				echo "<td width = '".$w."' height = '".$w."'>($i,$j)</td>";
			}
		}
		echo "</tr>";
	}
	echo "</table>";
}

//实例:初始化二维数组
$nr = 4; //行数
$nc = 4; //列数
for($i = 0; $i < $nr; $i++) {
	for($j = 0; $j < $nc; $j++) {
		$arr[$i][$j] = 0;
	}
} 
//八皇后结果输出
$tmp = myarr_unique(eight_queen($arr));
var_dump(count($tmp));
for($i = 0; $i < count($tmp); $i++) {
	display($tmp[$i]);
}

/*End of php*/

你可能感兴趣的:(N皇后问题摆法算法描述)