JakeLin-[蓝桥杯/ACM]2n皇后问题/8皇后问题-详细题解

题目描述

给定一个n*n的棋盘,棋盘中有一些位置不能放皇后。现在要向棋盘中放入n个黑皇后和n个白皇后,使任意的两个黑皇后都不在同一行、同一列或同一条对角线上,任意的两个白皇后都不在同一行、同一列或同一条对角线上。问总共有多少种放法?n小于等于8。 

输入

输入的第一行为一个整数n,表示棋盘的大小。 

接下来n行,每行n个0或1的整数,如果一个整数为1,表示对应的位置可以放皇后,如果一个整数为0,表示对应的位置不可以放皇后。 

输出

输出一个整数,表示总共有多少种放法。 

样例输入

4
1 1 1 1 
1 1 1 1 
1 1 1 1 
1 1 1 1 

样例输出

2

原题链接:[蓝桥杯][基础练习VIP]2n皇后问题

在我们学习2n皇后问题前,需要了解回溯法的一个经典题型:八皇后问题


一、首先贴上八皇后问题:

给定一个8*8的棋盘。现在要向棋盘中放入8个皇后使任意的两个皇后都不在同一行、同一列或同一条对角线上问总共有多少种放法?

我们需要明确:

  • 1、会有八个皇后被放入
  • 2、每一行放一个(也仅有一个)
  • 3、每一列也会有一个(也仅有一个)
  • 4、如果用全排列或者八个for循环,结果多半是惨烈的

关键代码:

int vis[10];   //vis[i]=1 表示第i【列】被访问过了,后面此列不被访问
int a[10];    //a[i]=j 表示第i个皇后(第i行)的位置为j->a[j]
void dfs(int step,int n) {   //安排第step个皇后(即第step【行】)
    if(step==n+1) {   //递归边界,表示n个皇后已经安排好了
        cnt++;
    } else {
        for(int i=1; i<=n; i++) {   //遍历i列
            if(vis[i]==0) {   //第i列必须是之前没访问过得一列
                bool tag = true;
                for(int j=1; j<step; j++) {   //遍历a数组1~step:查找与之前已经定好位的皇后是否冲突
                    if(step-j == i-a[j] || step-j == a[j]-i) {
                    //当前位置:(step,i)  之前位置(j,a[j]),若他们在对角线上,则斜率(step-j)/(i-a[j])为1或-1
                        tag = false;
                        break;
                    }
                }
                if(tag) {    //  i列符合条件
                    vis[i]=1;  // 访问
                    a[step] = i;
                    dfs(step+1,n);// 安排下一个皇后
                    vis[i]=0;  // 回溯:恢复DFS之前的状态
                }
            }
        }
    }
}

近似思路题型应用变形,可见 棋盘多项式-题解(C/C++描述)-详细解释


二、迁移到2n皇后中问题:

不同之处为:每一行除了有一个黑皇后外,还会有一个白皇后。

所以我们按照上述做法查找一个位置给黑皇后之后,还需要在查找一次,给白皇后定位,只有两位皇后都安排完毕了,才能进入下一行的定位
所以你会发现,不管是vis数组,还是皇后位置的数组,我们都需要准备两份

下面提供完整代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
int cnt = 0;
int map[10][10]; //存储地图
int visb[10];   //visb[i]=1第i列已经有了黑皇后 
int visw[10];   //白皇后,同上 
int black[10];  //black[i]=j 表示第i个黑皇后(i行)在第j列
int white[10];  //白皇后的位置,同上
void dfs(int step,int n) {  //step代表第几行,要安排第几个皇后
    if(step==n+1) {
        cnt++;
    } else {
        for(int i=1; i<=n; i++) {  //第step行遍历i列(i->1-n)
            bool black_tag = true;
            if(visb[i]==0 && map[step][i]==1) {
                for(int j=1; j<step; j++) { //跟之前已经定好位的皇后做比较
                    if(step-j == i-black[j] || step-j == black[j]-i) {  //斜对角方向 (step,i)和(j,a[j])的斜率为1或-1
                        black_tag = false;
                        break;
                    }
                }
                if(black_tag) {
                    visb[i]=1;  //这一列有皇后了,后面一整列都不能访问
                    black[step] = i;  //(step,i)->(step,a[step])
                    for(int k=1; k<=n; k++) {
                        if(k==i) continue;
                        bool white_tag = true;
                        if(visw[k]==0 && map[step][k]==1) {
                            for(int j=1; j<step; j++) { //跟之前已经定好位的皇后做比较
                                if(step-j == k-white[j] || step-j == white[j]-k) {  //斜对角方向 (step,k)和(j,a[j])的斜率为1或-1
                                    white_tag = false;
                                    break;
                                }
                            }
                            if(white_tag) {
                                visw[k]=1;
                                white[step] = k;
                                dfs(step+1,n);   //搜索下一行(下一个皇后)
                                visw[k]=0;
                            }
                        }
                    }
                    visb[i]=0;
                }
            }
        }
    }
}
int main() {
    int n;
    cin>>n;
    for(int i=1; i<=n; i++) {
        for(int j=1; j<=n; j++) {
            cin>>map[i][j];
        }
    }
    dfs(1,n);
    cout<<cnt;
    return 0;
}
发布了20 篇原创文章 · 获赞 15 · 访问量 216

猜你喜欢

转载自blog.csdn.net/qq_37414463/article/details/105391466