使用回溯法求解N皇后问题

N皇后问题

经典的N皇后问题描述为:在一个N x N的棋盘上放置N个皇后,要求任意的两个皇后都不在同一行、同一列或同一条对角线上,问在给定N的情况下有多少种放置的方法。

回溯法思路

求解N皇后最典型的方法是回溯法,此方法的思路可以概括为:在第一行占据一个位置,接着在下一行占据一个位置,判断两个位置之间是否存在冲突。如果不存在冲突,则继续在下下行占据一个位置,判断与前面的两个位置之间是否存在冲突,如果不存在冲突则继续往下占据位置,当占据到最后一行,且占据的位置与前N - 1行的位置均不冲突,那么便成功找到了一种放置皇后的方案。
如果在当前行占据的位置与前面的位置存在冲突,那么需要寻找当前行的其他位置,直到找到不冲突的位置并进入下一行寻找,或者当前行寻找完毕未发现不冲突的位置,那么则返回上上行,寻找上上行的其他位置,如果在上上行也没有找到,则继续向上寻找直到整个棋盘遍历完毕。

数据表示

由N皇后问题可知,在棋盘的每一行和每一列都只能放置一个皇后,那么我们在存储数据时便无需定义N x N的二维数组,直接定义长度为N的一维数组即可,即数组的索引可以表示行的值,而索引对应的值则表示列的值。

判断冲突方法

判断两个皇后之间是否存在位置冲突的简单方法为:判断横坐标是否相等,判断纵坐标是否相等,以及判断两者横坐标之差的绝对值是否等于纵坐标之差的绝对值。
可以这么理解,两者横纵坐标自然不能相等,而如果两者横坐标之差的绝对值等于纵坐标之差的绝对值,那么可以判定两者在一个正方形的斜对角上,即不满足不在对角线上这一条件。
举例来说即是,假设皇后A的坐标为 (xa, ya),皇后B的坐标为 (xb, yb),那么判断两者是否存在位置冲突的C/C++代码如下:

bool conflict= false;
if (xa == xb || ya == yb || abs(xb - xa) == abs(yb - ya)) {
	conflict = true;
}

方法实现

依照上述回溯法的思路,假设N = 8,则对应的C/C++代码如下:

#include <stdio.h>

inline int abs(int x)
{
	if (x < 0) {
		x = -x;
	}
	return x;
}

bool isConflict(int xa, int ya, int xb, int yb)
{
	bool result = false;
	if (xa == xb || ya == yb || abs(xb - xa) == abs(yb - ya)) {
		result = true;
	}
	return result;
}

void search(int rowIndex, int nCols, int* pos, int& cnt)
{
	for (int k = 0; k < nCols; k++) {
	    // 搜索当前行可行位置
		bool conflict = false;
		for (int n = 0; n < rowIndex; n++) {
			if (isConflict(n, pos[n], rowIndex, k)) {
				conflict = true;
				break;
			}
		}
		if (!conflict) {
		    // 记录可行位置列坐标
			pos[rowIndex] = k;
			// 当前行是最后一行则一次搜索成功
			if (rowIndex == nCols - 1) {
				cnt++;
			}
			else {
			    // 否则继续搜索下一行
				search(rowIndex + 1, nCols, pos, cnt);
			}
		}
	}
}

int main(int argc, char* argv[])
{
	int n = 8, pos[8], cnt = 0;
	search(0, n, pos, cnt);
	printf("%d\n", cnt);
	return 0;
}
  • 3
    点赞
  • 47
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值