一个n*n的国际象棋棋盘上放置n个皇后,这n个皇后两两均不在同一行、同一列、同一对角线上,求合法的方案数。
需要一层一层的搜索,因此采用深度优先搜索思想。
思考:n*n棋盘可用二维数组表示。已知约束条件:皇后均不在同一行、同一列、同一对角线上。
故编码寻找数学关系表达式。
解题一:考虑到每行只能放一个皇后,每列一个皇后,如果将每列皇后的行号依次写出,可以构成1~n的全排列。
只需要求解符合要求的全排列的方案数即可。枚举所有方案,然后判断每种情况是否合法,属于朴素算法,即暴力搜索。
1 #include<bits/stdc++.h>
2 using namespace std;
3
4 int n,ans=0;
5 bool flag=true; //标记这一组全排列是否可行
6 int num[1010]={0}; //num[i]表示第i行皇后所在第几列
7 int vis[1010]={0}; //判断数字是否用过
8
9 void dfs(int y){ //参数y表示第y层
10
11 //搜完一组全排列方案
12 flag=true; //初始化为true
13 if(y==n+1){
14 //num[i]两两比较,只需判断不同皇后是否在同一对角线即可,
15 //全排列已经保证了皇后的不同行不同列
16 for(int i=1;i<=n;i++){
17 for(int j=i+1;j<=n;j++){
18 if(abs(num[i]-num[j])==abs(i-j)){
19 flag=false;
20 }
21 }
22 }
23 if(flag){
24 ans++;
25 }
26 return;
27 }
28 //全排列递归部分
29 for(int i=1;i<=n;i++){
30 //判断数字是否使用过
31 if(!vis[i]){
32 vis[i]=true; //标记这一层深搜时数字i已使用
33 num[y]=i; //第y层使用数字i
34 dfs(y+1); //进行下一层搜索
35 vis[i]=false; //递归返回时清除本层标记,将数字i置为未使用
36 }
37 }
38 }
39 int main()
40 {
41 cin>>n;
42 dfs(1);
43 cout<<ans<<endl;
44 return 0;
45 }
优化一:当放置一部分皇后时,可能剩余的皇后无论怎样放置都不合法,因此就没必要向下递归了,直接返回上层即可。一般来说,如果在到达递归边界前的某层,由于一些事实导致已经不需要任何一个子问题递归,就可以直接返回上一层。一般把这种方法成为回溯法。
#include<bits/stdc++.h>
using namespace std;
int n,ans=0;
bool flag=true; //标记这一组全排列是否可行
int num[1010]={0}; //num[i]表示第i行皇后所在第几列
int vis[1010]={0}; //判断数字是否用过
void dfs(int y){ //参数y表示第y层
//搜完一组全排列方案
flag=true; //初始化为true
if(y==n+1){
//num[i]两两比较,只需判断不同皇后是否在同一对角线即可,
//全排列已经保证了不同行不同列
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(abs(num[i]-num[j])==abs(i-j) && i!=j){
flag=false;
}
}
}
if(flag){
ans++;
}
return;
}
//全排列递归部分
for(int i=1;i<=n;i++){
//判断数字是否使用过
if(!vis[i]){
vis[i]=true; //标记这一层深搜时数字i已使用
num[y]=i; //第y层使用数字i
dfs(y+1); //进行下一层搜索
vis[i]=false; //递归返回时清楚本层标记,将数字i置为未使用
}
}
}
int main()
{
cin>>n;
dfs(1);
cout<<ans<<endl;
return 0;
}
来源:https://www.cnblogs.com/mld-code-life/p/12293552.html