使用回溯法求解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
    评论
n 皇后问题指在 n×n 的棋盘上放置 n 个皇后,使得它们互相之间不会攻击到对方(即不在同一行、同一列或同一对角线上)。下面是使用回溯法求解 n 皇后问题的 C 代码: ```c #include <stdio.h> #include <stdlib.h> int n; // 皇后数量 int *pos; // 存储每行皇后所在的列号 // 判断当前位置是否合法 int is_valid(int row, int col) { for (int i = 0; i < row; i++) { if (pos[i] == col || abs(i - row) == abs(pos[i] - col)) { return 0; } } return 1; } // 回溯求解 void solve(int row) { // 所有皇后已经放置完毕,输出解 if (row == n) { for (int i = 0; i < n; i++) { printf("%d ", pos[i]); } printf("\n"); return; } // 尝试在当前行的每个位置放置皇后 for (int col = 0; col < n; col++) { if (is_valid(row, col)) { pos[row] = col; solve(row + 1); } } } int main() { printf("请输入皇后数量:"); scanf("%d", &n); pos = (int *)malloc(n * sizeof(int)); solve(0); free(pos); return 0; } ``` 在程序中,首先输入皇后数量 n,然后动态分配数组 pos 来存储每行皇后所在的列号。回溯求解函数 solve 从第 0 行开始尝试在每个位置放置皇后,如果当前位置合法,则将皇后放在该位置,并递归进入下一行求解。如果所有皇后都已经放置完毕,则输出解。函数 is_valid 用来判断当前位置是否合法,即该位置是否与之前已经放置的皇后冲突。当所有解都被找到后,释放 pos 数组的空间。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值