n皇后问题
一个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;
}