c++回溯算法——N皇后、2N皇后问题

本文详细解析了N皇后问题的两种经典场景及其解决方案。首先介绍了如何使用深度优先搜索算法解决标准N皇后问题,即在n×n的棋盘上放置n个皇后,确保彼此不受攻击。随后,探讨了更复杂的2N皇后问题,考虑了棋盘上某些位置不可用的情况,并展示了如何通过改进的深度优先搜索算法找到所有可能的放置方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

N皇后
问题描述:

  • 在n×n格的棋盘上放置彼此不受攻击的n个皇后。
  • 按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。n皇后问题等价于在n×n格的棋盘上放置n个皇后,任何两个皇后不放在同一行或同一列或同一斜线上。
  • 编程要求:找出一个n×n格的棋盘上放置n个皇后并使其不能互相攻击的所有方案。
    -在这里插入图片描述
#include <bits/stdc++.h>
using namespace std;
#define NUM 20
int n;			//棋盘的大小
int x[NUM];		//解向量
int sum;		//当前已经找到的可行方案数
//形参t是回溯的深度
inline bool Place(int t)
{
    int i;
    for(i=1;i<t;i++)
    {
    /*1.由于每一列只放置一个皇后,所以不会出现同列的问题,不用判断合法性。
    2.对于每一行,假设已经放置到t列,只要判断 ,i=1, 2, …, t-1互不相同即可。
   3. 对于对角线的判断,可以看成是斜率为±1的两条直线,经过两点(i,x[i])和(t,x[t]):
   |t-i/(x[t]-x[i])|=1;(t-i)的绝对值=(x[i]-x[t])的绝对值
    */
        if((abs(t-i)==abs(x[i]-x[t]))||(x[i]==x[t]))
            return false;
    }
     return true;
}
//形参t是回溯的深度,从1开始
void Backtrack(int t)
{
    int i;
    //到达叶子节点,获得一个方案,累加
    if(t>n)
    {
        sum++;
        for(i=1;i<=n;i++)
        {//输出该方案
            cout<<x[i]<<" ";
        }
        cout<<endl;
    }else
        for(i=1;i<=n;i++)
        {
            x[t]=i;
            if(Place(t))Backtrack(t+1);
        }

}
int main()
{
    cin>>n;
    Backtrack(1);
    cout<<"Total:"<<sum<<endl;
    return 0;
}
/*DFS深度优先搜索算法(depth first search):一种用于遍历或搜索树或图的算法。
 沿着树的深度遍历树的节点,尽可能深的搜索树的分支。
 当节点v的所在边都己被探寻过或者在搜寻时结点不满足条件,
 搜索将回溯到发现节点v的那条边的起始节点。
 整个进程反复进行直到所有节点都被访问为止。
 属于盲目搜索,最糟糕的情况算法时间复杂度为O(!n)。*/

2N皇后
问题描述:
给定一个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
样例输入
4
1 0 1 1
1 1 1 1
1 1 1 1
1 1 1 1
样例输出
0

#include <bits/stdc++.h>
using namespace std;
const int maxn=9;
int n;
int Q[maxn][maxn];
int black[maxn]={0};
int white[maxn]={0};
int sum;//当前已经找到的可行方案数

bool placeBlack(int t)
{
    for(int i=1;i<t;i++)
    {
        if(abs(black[i]-black[t])==abs(i-t)||black[i]==black[t])
            return false;
    }
    return true;
}
bool placeWhite(int t)
{
    for(int i=1;i<t;i++)
    {
        if(abs(white[i]-white[t])==abs(i-t)||white[i]==white[t])
            return false;
    }
    return true;
}
void dfs_white(int t)
{
    if(t>n)
    {
        sum++;
    }
    for(int i=1;i<=n;i++)
    {
        if(black[t]==i)//表示第t列的第i行位置已经被黑皇后占用
            continue;
        if(Q[t][i]==0)//判断前提条件是否成立
            continue;
        white[t]=i;//把第t列的白皇后放在i行
        if(placeWhite(t))dfs_white(t+1);//如果可以放,则递归
    }
}
void dfs_black(int t)
{
    if(t>n)//黑皇后处理完再处理白皇后
    {
       dfs_white(1);
    }
    for(int i=1;i<=n;i++)
    {
        if(Q[t][i]==0)//判断前提条件是否成立
            continue;
        black[t]=i;//把第t列的黑皇后放在i行
        if(placeBlack(t))dfs_black(t+1);//如果可以放,则递归
    }
}
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            cin>>Q[i][j];//定义棋盘
        }
    }
    sum=0;
    dfs_black(1);
    cout <<sum<< endl;
    return 0;
}
<think>嗯,用户想了解如何使用递归算法解决N皇后问题,需要解决方案和示例代码。首先,我得回忆一下N皇后问题的基本概念。这个问题是在一个N×N的棋盘放置N个皇后,使得它们互不攻击,即不能在同一行、同一列或同一对角线上。 递归算法通常用于这类回溯问题,因为可以逐行放置皇后,并检查当前位置是否安全。如果当前行放置的位置有效,就递归处理下一行;如果无效,则回溯到上一步,尝试其他位置。 接下来,我需要考虑如何表示棋盘。通常使用一个一维数组,其中索引代表行号,值代表该行的皇后所在的列。例如,board[row] = col表示在第row行的第col列放置皇后。 然后是关键的安全检查函数。需要检查当前列是否已被占用,以及两个对角线方向是否有其他皇后。检查列可以直接遍历之前的行,看是否有相同的列值。对角线检查可以通过判断行的差和列的差的绝对值是否相等来实现。 接下来是递归函数的设计。递归的终止条件是当处理完所有行(即row == N),此时将当前棋盘状态加入结果列表。否则,遍历当前行的每一列,如果安全,则放置皇后,递归调用下一行,然后回溯,移除当前列的皇后,尝试下一个位置。 用户提供的引用中有提到回溯和递归的结合使用,比如引用[3]提到回朔法递归,而引用[4]中的递归入口和回溯机制。示例代码可能类似于引用中的Python实现,但需要确保语法正确,比如使用列表来存储结果,并在每次递归调用后恢复棋盘状态。 此外,需要考虑如何将棋盘转换为用户所需的输出式,例如用'.'和'Q'表示每个位置。这通常在生成结果时处理,将一维数组转换为二维的字符串列表。 测试代码部分,比如N=4时,应该有两个解,可以通过打印输出来验证。同时,要处理边界情况,比如N=0或1的情况,确保代码的健壮性。 可能的问题点包括递归的深度和效率,但对于教学示例来说,重点在于清晰而非优化。用户可能需要理解递归如何隐式地维护了回溯的状态,如引用[1]中提到的“自动push、pop的堆栈”。 最后,生成相关问题时要围绕递归的应用、优化方法、时间复杂度分析等,帮助用户进一步深入学习。</think>### 递归实现N皇后问题解决方案 #### 问题描述 在$N \times N$的棋盘放置$N$个皇后,要求任意两个皇后不能互相攻击(即不在同一行、同一列或同一对角线上)。递归算法通过回溯思想实现所有可行解的搜索[^1]。 --- #### 解决思路 1. **递归框架**:逐行放置皇后,每行选择一个安全位置,递归处理下一行 2. **冲突检测**:检查当前放置位置是否与已放置皇后产生冲突 3. **回溯机制**:当某行无法找到安全位置时,回退到上一行重新选择位置 --- #### 实现步骤 1. 使用一维数组`board`存储每行皇后的列坐标(索引为行号,值为列号) 2. 递归函数参数包含当前处理的行号`row` 3. 终止条件:当`row == N`时记录有效解 4. 遍历当前行的所有列,检查安全性后递归处理下一行 --- #### Python示例代码 ```python def solve_n_queens(n): def is_safe(row, col): # 检查列冲突和对角线冲突 for r in range(row): if board[r] == col or \ abs(r - row) == abs(board[r] - col): return False return True def backtrack(row): if row == n: # 生成棋盘式 solution = [] for col in board: solution.append('.' * col + 'Q' + '.' * (n - col - 1)) res.append(solution) return for col in range(n): if is_safe(row, col): board[row] = col backtrack(row + 1) board[row] = -1 # 回溯 res = [] board = [-1] * n # 初始化棋盘 backtrack(0) return res # 测试示例(N=4) print(solve_n_queens(4)) ``` --- #### 代码解析 1. **数据结构**:`board`数组存储每行皇后位置,索引为行号,值为列号 2. **冲突检测**: - 列冲突:`board[r] == col` - 对角线冲突:行差绝对值等于列差绝对值($|r - row| == |board[r] - col|$) 3. **递归过程**: - 当完成最后一行放置(`row == n`)时记录解 - 通过回溯撤销无效的放置尝试[^3] --- #### 时间复杂度 - 最坏情况:$O(N!)$(实际通过剪枝优化后远小于此值) - 空间复杂度:$O(N)$(递归栈深度为N) ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值