先来说一下思路
八皇后是一个经典的dfs题,这个棋盘就是一个8*8的方格图,我们来对这个图按行进行dfs。既然题目告诉了只有92组正确答案,而输出是询问解的字典序大小,所以可以先把所有92个解按字典序先全部存在string数组里,这样答案就可以直接通过下标访问输出。那么如何来获得这92组解呢。
string res[100]; //用来按顺序存放结果
既然是字典序的答案,所以可以从第1行第1列开始dfs,遍历该行的每一列,然后把该行该列所在的行列和对角线全部标记,依次dfs第2行,第3行。。。。。。。
int book[8][8]; //用来做标记
但是这里有点小技巧,可以不要标记所在的行,因为下一行遍历行判断标记的时候与上一行的标记没有关系,所以只要标记所在的列和他所在的对角线,对角线也只要从他下一行开始标记。我们可以用行做dfs结束的判断条件,当dfs到第8行时如果能还能进入下一层,那么解就是达到第9行(这个图没有第9行)时的那个字符串,直接保存这个字符串然后return就可以了,每次dfs的时候传递行数和当前dfs深度时的字符串。
1 void dfs(int n,string t){//n是行数,t是获取到上一行时的字典序字符串
2 //这里代码是从0行开始的
3 if(n==8){ //如果过了第7行,说明得到一个可行解,把这个解存下来
4 res[cnt++]=t;
5 return;
6 }
7
8 for(int i=0;i<8;i++){//遍历列
9
10 if(!book[n][i]){ //判断改点是否被标记
11
12 for(int k=n+1;k<8;k++){ //做标记
13 book[k][i]++;
14 if(i+k-n<8){ //判断是否越界
15 book[k][i+k-n]++;
16 }
17 if(i-(k-n)>=0){ //判断是否越界
18 book[k][i-(k-n)]++;
19 }
20 }
21
22 dfs(n+1,t+char(i+49));//传递行数和当前dfs深度时的字符串。
23
24 for(int k=n+1;k<8;k++){ //回溯
25 book[k][i]--;
26 if(i+k-n<8){
27 book[k][i+k-n]--;
28 }
29 if(i-(k-n)>=0){
30 book[k][i-(k-n)]--;
31 }
32 }
33 }
34 }
35 }
这里做标记的时候没有让book数组直接等于一个数而是让他自增自减是因为后面做的标记可能会和前面做的标记重叠,后面行的对角线标记和前面行的行标记重叠,自增自减就可以避免回溯去标记的时候把前面行的标记去掉,这里的对角线标记是通过这些变量的关系从下一行开始以所在列为基准依次+1或-1获取列(并且判断是否越界)的。
附上完整AC代码
#include<iostream>
using namespace std;
string res[100]; //用来按顺序存放结果
int book[8][8]; //用来做标记
int cnt=0;
void dfs(int n,string t){
if(n==8){ //如果过了第7行,说明得到一个可行解,把这个解存下来
res[cnt++]=t;
return;
}
for(int i=0;i<8;i++){
if(!book[n][i]){ //判断改点是否被标记
for(int k=n+1;k<8;k++){ //做标记
book[k][i]++;
if(i+k-n<8){ //判断是否越界
book[k][i+k-n]++;
}
if(i-(k-n)>=0){ //判断是否越界
book[k][i-(k-n)]++;
}
}
dfs(n+1,t+char(i+49));
for(int k=n+1;k<8;k++){ //回溯
book[k][i]--;
if(i+k-n<8){
book[k][i+k-n]--;
}
if(i-(k-n)>=0){
book[k][i-(k-n)]--;
}
}
}
}
}
int main(){
int n;
cin>>n;
string t;
dfs(0,t);//从第0行开始dfs,开始传递一个空的字符串
while(n){
n--;
int t;
cin>>t;
cout<<res[t-1]<<endl;//直接输出答案
}
return 0;
}
ACM小白,第一次写题解,有诸多不足,欢迎指正。
原文:https://www.cnblogs.com/anitena/p/11299997.html