心路历程:
之前并没有系统的学过递归和搜索,在学这道题前先学习了深搜、递归,并敲掉了经典N皇后,因此理解起来难度也不是很大。 没有基础的同学一定要先把N皇后看会在碰这道题。下面会有N皇后逐步讲解的视频链接和N皇后源码。如果大家已经学会N皇后,自行跳过这一步,直接看2N皇后源码就行了,代码里有详细的注释。
传送门→N皇后问题(N Queens) 从示意图到代码
PS:这个视频较长,看到理解回溯的原理就可以了。
经典N皇后源码:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 15; //这种定义方法比#define要好
int q[maxn];
int m, sum = 0;
//place函数:这个位置是否可以放置
int place(int k) {
for(int i = 1; i < k; i++) //当前行皇后是否与前K行皇后有冲突
//若当前皇后所在行数-某一行皇后所在行数=当前皇后所在列数-某一行皇后所在咧数。 或 当前皇后所在列数=某一行皇后所在列数。 则不符合。
if(abs(k-i) == abs(q[k]-q[i]) || q[k] == q[i])
return 0;
return 1;
}
void dfs(int step) {
if(step > m) { sum++; return; } //终止条件
for(int i = 1; i <= m; i++) {
q[step] = i; //q数组里的值是该皇后所在列数
if(place(step)) dfs(step+1);
}
}
int main() {
int n; cin >> n; while(n--) {
sum = 0;
cin >> m;
dfs(1);
cout << sum << endl;
}
return 0;
}
解2N皇后的注意点:
1、独立理解出n皇后回溯解法。
2、本题特点:在原有n皇后的基础上加了一个判断,如果是1则可以走, 如果是0,无论如何,这个点都无法参与运算。
3、2n皇后特点:判断完黑皇后的位置后,将黑皇后所在位置置0,判断白皇后,若在此种情况下白皇后也可以全部放置,则可能的组合+1。
2N皇后源码:
#include<bits/stdc++.h>
using namespace std;
int queen[8][8]; //棋盘
int A[8], B[8]; //分别存放白、黑皇后可取的列数。
int cnt, t; //cnt=可能数,t=棋盘尺寸
void dfs_black(int ccur) { //ccur=0,从0开始回溯
if(ccur == t) cnt++;
else for(int i = 0; i < t; i++) {
if(queen[ccur][i]) { //1为可以放置
B[ccur]=i; //存入当前列
int ok = 1;
for(int j = 0; j < ccur; j++) //判断前curr行是否有冲突
//若当前皇后列数=某一行皇后列数 或 当前行皇后所在行-当前皇后所在的列=某一行皇后所在行-某一行皇后所在的列
//写法2:if(B[ccur]==B[j]||ccur-B[ccur]==j-B[j]||ccur+B[ccur]==j+B[j])
if(B[ccur]==B[j]||abs(ccur-j)==abs(B[ccur]-B[j]))
ok = 0; //则该位置不可取
if(ok) dfs_black(ccur+1); //若可取,则递归至curr+1
}
}
}
void dfs_white(int cur) {
if(cur == t) { //若白皇后遍历完则执行,否则跳到else
for(int k = 0; k < t; k++)
queen[k][A[k]]=0; //该位置放了白皇后,不可以放别的。
dfs_black(0);
for(int k = 0; k < t; k++)
queen[k][A[k]]=1; //状态重置很重要
}
else for(int i = 0; i < t; i++){ //从第0行开始遍历
if(queen[cur][i]) { //若可以放置(为1)
A[cur]=i; //则放置
int ok = 1;
for(int j = 0; j < cur; j++)
if(A[cur]==A[j]||abs(cur-j)==abs(A[cur]-A[j]))
ok = 0;
if(ok) dfs_white(cur+1);
}
}
}
int main() {
cin >> t; //皇后的个数及行/列数
for(int i = 0; i < t; i++)
for(int j = 0; j < t; j++)
cin >> queen[i][j]; //输入棋盘
dfs_white(0); //从0开始回溯
cout << cnt << endl;
return 0;
}