赞
踩
在一个n*n的棋盘放置n个皇后,使皇后彼此之间不能相互攻击,满足:
1、同一行只能有一个皇后
2、同一列只能有一个皇后
3、每个皇后所在的主次对角线只有一个皇后,即它本身
共有多少种满足要求的摆法?
例如:
**.*
.***
***. 这是一种4*4中的一种摆放方式,其中'*'代表没有放置,'.'代表
*.** 放置了皇后
可以从上往下依次摆放皇后,对于每一行,检查该行每一列是否满足摆放的
要求,如若满足,继续摆放下一行,直至每一行都有符合要求的皇后,得到
一种可行的摆放方案,然后通过回溯尝试不同的可能性。
1.首先定义一个n*n的二维数组,为方便,可以将cheeboard[i][k]=0定义
成未放皇后,1为放置了皇后
2.然后调用backtracking函数,用for循环从第一行开始对每一列测试放皇
后,当然首先要满足那三个要求,这里可以用isValid函数对该位置能不能放
皇后进行判断,如若返回真,则将cheeboard[i][k]赋值为1,并调用
backtracking函数进行下一行皇后的摆放,最后回溯将cheeboard[i][k]
赋值为0,看看该行其余列是否可以放皇后
3.对于backtracking的返回条件,当然是当row(当前所在行) = n,即已经
将每一行都放置了皇后后结束,同时sum++,这样就找到了一种摆法,通过不断
重复这些操作,即可将所有情况找出来。
#include <bits/stdc++.h> using namespace std; const int MAXN = 10; int sum = 0; // 摆放的总数 bool isValid(int chessboard[MAXN][MAXN], int n, int row, int col){ for (int i = 0; i < row; i ++) { // 判断是否同列,因为是从上往下一行一行来的,所以1到row-1即可 if (chessboard[i][col] == 1) // 且不用再判断是否是同一行了 return false; } for (int i = row - 1, k = col - 1; i >= 0 && k >= 0; i --, k --) { if (chessboard[i][k] == 1) // 判断与主对角线平行的对角线,即左上的斜线 return false; } for (int i = row - 1, k = col + 1; i >= 0 && k < n; i --, k ++) { if (chessboard[i][k] == 1) // 判断与次对角线平行的对角线,即右上的斜线 return false; } return true; } void backtracking(int chessboard[MAXN][MAXN], int n, int row){ if(row == n) { // 所有行都放了皇后 sum ++; return; } for (int i = 0; i < n; i ++) { // 对该行每一列测试放皇后 if (isValid(chessboard, n, row, i)) { chessboard[row][i] = 1; backtracking(chessboard, n, row + 1); // 进行下一行的放置 chessboard[row][i] = 0; // 回溯 } } } int main() { int n; cin >> n; int chessboard[MAXN][MAXN] = {0}; backtracking(chessboard, n, 0); cout << sum; return 0; }
上面的方法是通过对每一行每一列依次判断的,并且isValid用了三个for 循环,因此会有点浪费时间,那么有没有什么可以优化的地方呢?答案是有 的。 我们可以将二维数组直接降为一维数组,通过对二维数组的定义,可以发现, 只有放置了皇后的才置为了1,其余都为0。那么实际上我们可以定义一个 result数组,其中result[i] = k表示第i行的第k列放置了皇后(假设 下标从0开始) 那么可以通过for循环对result[i]不同的赋值(0到n-1)实现在每一行的每一 列测试放置皇后,同时isValid函数中可以只用一个for循环解决,其中判断 条件为(result[i] = col || abs(row-i) == abs(col-result[i])), 前者判断该列是否已经有了皇后(result[i]存储的是第i行的皇后位置,后者 判断该位置(row,col)的同一斜线是否有皇后,因为如果在同一斜线上,行数 之差等于列数之差 这样,我们就实现了优化,下面是代码:
#include <bits/stdc++.h> using namespace std; const int MAXN = 10; int sum = 0; // 摆放的总数 int result[MAXN] = {0}; bool isValid(int n, int row, int col){ for (int i = 0; i < row; i ++) { // 判断是否同列,因为是从上往下一行一行来的,所以1到row-1即可 if (result[i] == col || abs(row - i) == abs(col - result[i])) return false; } return true; } void backtracking(int n, int row){ if(row == n) { // 所有行都放了皇后 sum ++; return; } for (int i = 0; i < n; i ++) { // 对该行每一列测试放皇后 if (isValid(n, row, i)) { result[row] = i; // 注意这里的写法,表示第row行第i列放置了皇后 backtracking(n, row + 1); // 进行下一行的放置 result[row] = 0; // 回溯 } } } int main() { int n; cin >> n; backtracking(n, 0); cout << sum; return 0; }
1.洛谷P1219 [USACO1.5]八皇后 Checker Challenge
2.力扣51–n皇后(该题需要知道一些vector的用法)
3.蓝桥杯官网–受伤的皇后
每道题问的不一定一样,需要根据具体题目灵活变通,n皇后问题最重要的是理解其中的回溯思想。希望这篇文章有所帮助!qwq
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。