八皇后问题

1.什么是八皇后问题?
八皇后问题是一个以国际象棋为背景的问题:如何能够在 8×8 的国际象棋棋盘上放置八个皇后,使得任何一个皇后都无法直接吃掉其他的皇后?为了达到此目的,任两个皇后都不能处于同一条横行、纵行或斜线上。八皇后问题可以推广为更一般的n皇后摆放问题:这时棋盘的大小变为n×n,而皇后个数也变成n。而且仅当 n = 1 或 n ≥ 4 时问题有解。
八皇后问题最早是由国际西洋棋棋手马克斯·贝瑟尔于1848年提出。之后陆续有数学家对其进行研究,其中包括高斯和康托,并且将其推广为更一般的n皇后摆放问题。八皇后问题的第一个解是在1850年由弗朗兹·诺克给出的。诺克也是首先将问题推广到更一般的n皇后摆放问题的人之一。1874年,S.冈德尔提出了一个通过行列式来求解的方法,这个方法后来又被J.W.L.格莱舍加以改进。
艾兹格·迪杰斯特拉在1972年用这个问题为例来说明他所谓结构性编程的能力。
八皇后问题出现在1990年代初期的著名电子游戏第七访客中。



2.求解思路:
问题比较显而易见的是,一共要放置8个皇后.每行放置一个,在每一行放置皇后时,初始有8个位置可以放置。但是由于前面已经放置皇后的限制,要在8个位置上寻找到可以放置的位置。直到8行均找到放置位置,问题有一个解。在某一行如果无法找到放置位置,需要回溯到上一行,在上一行已经找到位置的基础上寻找下一个可以放置的位置,继续进行这个过程。如果要找到所有可行解,在找到第一个解后,继续需找最后一行的下一个可以放置位置。直到回溯到第一行无法找到可以放置位置,即找到全部解。

3.非递归实现:
伪代码:
1.初始化一个二维数组,每个位置置0,放置皇后置1.
2.依次对每一行寻找放置皇后的位置,如果可以放置,将位置置1,继续下一行;否则,回溯到上一行的下一个位置
进行判断,并将上一个可以放置位置置0.重复此过程,直到所有行均有可放置位置.打印一组解.
3.找到一组解后,试图在最后一行下个位置寻找解,直到回溯到第一行也没有可以放置位置.即找到所有解.
struct point
{
	int x;
	int y;
};

int Eight[][8] = {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};
bool Put(int array[][8], point pt)
{
	for (int i = 0; i<8; i++) // 横坐标
	{
		for (int j = 0; j<8; j++) // 纵坐标
		{
			if (array[i][j] == 1 ) // 如果当前坐标有皇后,根据此点判断传入点是否可以放置
			{
				if(pt.x == i || pt.y == j) // 判断是否在同一行或者同一列
					return false;
				// 每个点四个方向
				// 左上
				/*int m = i;
				int n = j;
				while ((--m>=0) && (--n >=0))
				{
					if (pt.x == m && pt.y == n)
					{
						return false;
					}
				}
				// 右上
				m = i;
				n = j;
				while ((--m>=0) && (++n) <= 7)
				{
					if (pt.x == m && pt.y == n)
					{
						return false;
					}
				}*/
				// 左下
				int m = i;
				int n = j;
				while ((++m <= 7) && (--n >= 0))
				{
					if (pt.x == m && pt.y == n)
					{
						return false;
					}
				}
				// 右下
				m = i;
				n = j;
				while (++m<=7 && ++n <= 7)
				{
					if (pt.x == m && pt.y == n)
					{
						return false;
					}
				}

			}

		}
	}
	return true;
}
// 打印八皇后的解
void Print()
{
	for (int m = 0; m<8; m++)
	{
		for (int n = 0; n<8; n++)
		{
			printf("%d, ", Eight[m][n]);
		}
		printf("\r\n");
	}
}
	int nCount = 0;
	for(int i = 0; i<8; i++)
	{
		bool bFind = false; // 标识当前行是否找到解
		int j = 0;
		// 每次寻找插入位置时,判断是否当前行已经有放置皇后的位置,如果有
		// 需要从下一个位置开始寻找,并将原位置置0
		for (int k = 0; k<8; k++)
		{
			if (Eight[i][k] == 1)
			{
					j = k + 1;
					Eight[i][k] = 0;
					break;
			}
		}
		for (j; j<8; j++)
		{
			point pt;
			pt.x = i;
			pt.y = j;
			if(Put(Eight, pt)) // 如果找到了可以放置的坐标,寻找下一行
			{
				bFind = true;
				Eight[pt.x][pt.y] = 1;
				break;
			}else
			{
				continue;
			}
		}
		if (bFind) // 不用回溯
		{
			if (i == 7) // 找到一种解法
			{
				nCount++;
				Print();
				i--;//试图找到下一种解法
				printf("%d\r\n", nCount);
				//getchar();
			}

		}else // 回溯到上一层
		{
			i -= 2;
			if(i < -1)
			{
				printf("over");
				getchar();
			}
		}
	}
4.递归实现
static int gEightQueen[8] = { 0 }, gCount = 0;
void print()//输出每一种情况下棋盘中皇后的摆放情况
{
	for (int i = 0; i < 8; i++)
	{   
		int inner;
		for (inner = 0; inner < gEightQueen[i]; inner++)
			cout << "-";
		cout <<"#";
		for (inner = gEightQueen[i] + 1; inner < 8; inner++)
			cout << "-";
		cout<<endl;		
	}
	cout << "==========================\n";
}

/* loop表示在第几行放置, value当前放置的位置
0表示当前位置不可用,1表示当前位置可以放置*/
int check_pos_valid(int loop, int value)//检查是否存在有多个皇后在同一行/列/对角线的情况
{
	int index;
	int data;
	for (index = 0; index < loop; index++) // 对前面每个元素的位置进行判断,看是否符合要求
	{
		data = gEightQueen[index];
		if (value == data) // 同一列
			return 0;
		if ((index + data) == (loop + value)) // 左下对角线
			return 0;
		if ((index - data) == (loop - value)) // 右下对角线
			return 0;
	}
	return 1;
}
void eight_queen(int index)
{
	int loop;
	for (loop = 0; loop < 8; loop++) // 对8个位置进行判断
	{
		if (check_pos_valid(index, loop))
		{
			gEightQueen[index] = loop;
			if (7 == index)
			{
				gCount++, print();
				gEightQueen[index] = 0; // 将当前位置置0,试图寻找下一个解
				return;
			}
			eight_queen(index + 1);
			// 没有找到,上面返回,将上一层置0,重新寻找
			gEightQueen[index] = 0;
		}
	}
}








评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值