Bootstrap

N皇后问题-回溯法

问题描述

  在N*N的方格棋盘放置了N个皇后,使得它们不相互攻击(即任意2个皇后不允许处在同一排,同一列,也不允许处在与棋盘边框成45角的斜线上。你的任务是,对于给定的N,求出有多少种合法的放置方法。

输入格式

  输入中有一个正整数N≤10,表示棋盘和皇后的数量

输出格式

  为一个正整数,表示对应输入行的皇后的不同放置数量。

样例输入

5

样例输出

10

思路:

        从第一个点开始枚举所有可能,判断当前位置是否可以存放一个皇后,若可以则进入下一行继续枚举。我们并不知道有多少n,也就不能确定多少层循环,所以要用到dfs去搜索所有可能。

建立一个nxn的矩阵chess存储棋盘,1代表有棋子,0代表没有棋子,初始化棋盘。

从第一行第一列开始,存放一个皇后,进入第二行,判断可以存放皇后的点,继续存放,进入下一行……直到本行没有点可以放皇后或者行数大于n。

  • 若本行没有点可以存放皇后,则返回上一层
  • 若已到最后一层,则计数变量count++,返回上一层

不能存放棋子的条件:

        任意2个皇后不允许处在同一排,同一列,也不允许处在与棋盘边框成45角的斜线上

当前点坐标为(x,y)

∀i,j∈[1,n],当chess[i][j]==1时

  • x==i && y==j 代表当前坐标的横向或纵向上有皇后
  • |x-i|==|y-j| 代表当前坐标的45°或135°方向上有皇后

以上任意一种情况都不能存放皇后

回溯时需将当前行以下所有行初始化,并将当前点的皇后拿走。

附上代码

#include <iostream>
#include <cstring>
#include <cmath>
using namespace std;
int chess[11][11], count = 0;//棋盘以及可能的个数
void backtracking(int N, int layers);//回溯函数
bool search(int x, int y, int N);

int main() {
	int N;
	cin >> N;
	memset(chess, 0, sizeof(chess));//初始化棋盘
	backtracking(N, 1);
	cout << count;
}

void backtracking(int N, int layers) {
	int x = 1;//x用来遍历layers层的每个点

	if (layers > N)//若当前层数大于N就不再继续
		return;

	while (x <= N) {
		if (x < N) {//当前行未放棋子,下面所有行必定没有棋子
			for (int i = layers + 1; i <= N; i++) {
				for (int j = 1; j <= N; j++) {
					chess[i][j] = 0;
				}
			}
		}
		if (layers > 1 && search(layers, x, N)) {
			x++;
			continue;
		}

		chess[layers][x] = 1;//当前点放一个棋子

		if (layers == N) {
			count++;
			x++;
			continue;
		}

        //进入下一层
		backtracking(N, layers + 1);
        //从下一层回溯上来,取走当前点的皇后
		chess[layers][x] = 0;
		x++;
	}
}
bool search(int x, int y, int N) {
	for (int i = 1; i <= x; i++) {
		for (int j = 1; j <= N; j++) {
			if (chess[i][j] == 1 && (labs(x - i) == labs(y - j) || (i == x || j == y))) {
                //|x-i| == |y-j| 即可表达该点的45°与135°上的点
				return false;
			}
		}
	}
	return true;
}

;