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;
}

posted @ 2020-02-11 00:08  三行代码划江湖  阅读(311)  评论(0编辑  收藏  举报