题目
问题描述:
N皇后问题是一个经典的回溯算法问题。在一个N×N的棋盘上放置N个皇后,使得它们互不攻击。即任何两个皇后都不能处于同一行、同一列或同一斜线上。你的任务是找出所有可能的放置方案。
输入:
一个整数N,表示棋盘的大小和皇后的数量。
输出:
所有可能的皇后放置方案。每个方案应该是一个N×N的矩阵,其中数字1到N表示皇后的位置,0表示空位置。方案之间应该用换行符分隔。
示例输入:
4
示例输出:
1
0 1 0 0
0 0 0 1
1 0 0 0
0 0 1 0
2
0 0 1 0
1 0 0 0
0 0 0 1
0 1 0 0
...(其他可能的方案)
解释
在编程中,深度优先搜索(DFS)是一种常用的算法,用于遍历或搜索树或图的节点。今天,我们将通过一个具体的例子,来探索DFS在解决特定问题上的妙用。这个问题是关于在一个棋盘上放置棋子,要求棋子的位置满足一定的条件。
首先,让我们来了解一下这段代码的整体结构和目标。代码定义了一个二维数组a
来表示棋盘,n
表示棋盘的行数和列数,flag
数组用来标记哪些列已经被占用,cnt
用来记录找到的符合条件的棋盘摆放方式的数量。
代码的核心在于check
函数和dfs
函数。check
函数用于检查在(x, y)
位置放置棋子是否违反了给定的条件,即不能与其他棋子在同一主对角线或副对角线上。dfs
函数则是深度优先搜索的实现,用于递归地尝试在棋盘上放置棋子,并调用check
函数来确保每一步都是合法的。
pt
函数用于输出当前棋盘的状态,并增加cnt
的计数。当dfs
函数递归到最后一行时,说明已经找到了一种合法的摆放方式,此时会调用pt
函数输出棋盘状态。
main
函数是程序的入口点,它首先读取棋盘的大小n
,然后调用dfs
函数从第一行开始递归地尝试放置棋子。
这个算法的核心思想是通过DFS来穷举所有可能的棋子摆放方式,同时利用check
函数来确保每一步都是合法的。这种方法在处理类似的问题时非常有效,尤其是在解空间相对较小的情况下。
然而,值得注意的是,当棋盘的大小n
增大时,解空间会迅速增长,可能导致算法的运行时间变得非常长。在实际应用中,可能需要考虑优化算法或采用其他更高效的方法来解决这类问题。
总的来说,这段代码通过深度优先搜索展示了如何在一个棋盘上按照特定条件摆放棋子。它不仅展示了DFS在解决具体问题时的应用,也为我们提供了一种解决类似问题的思路和方法。无论是初学者还是有一定编程经验的开发者,都可以通过学习和分析这段代码来加深对DFS算法的理解和应用。
代码结构
代码整体结构清晰,由全局变量定义、辅助函数、搜索函数和主函数组成。
-
全局变量
a[55][55]
:二维数组,用于存储棋盘状态,a[i][j]
为1表示第i行第j列放置了皇后,为0表示没有放置。flag[55]
:一维数组,用于标记列的状态,flag[i]
为1表示第i列已被占用,为0表示未被占用。n
:棋盘的大小,即棋盘的行数和列数。cnt
:计数器,用于记录符合条件的棋盘摆放方式的数量。
-
辅助函数
check(int x, int y)
:该函数用于检查在位置(x, y)
上是否可以放置皇后。它通过遍历整个棋盘,检查当前位置是否与已放置的皇后在同一行、主对角线或副对角线上。 -
搜索函数
dfs(int x)
:该函数是深度优先搜索的核心。它从第x
行开始尝试放置皇后,通过递归调用自身来放置下一行的皇后。当x
等于n+1
时,表示所有行都成功放置了皇后,此时调用pt()
函数输出棋盘状态。 -
输出函数
pt()
:该函数用于打印当前棋盘状态,并增加计数器cnt
的值。 -
主函数
main()
:程序的入口点。它首先读取棋盘的大小n
,然后调用dfs(1)
从第一行开始递归地尝试放置皇后。
代码逻辑与实现
- 初始化:在
main()
函数中,程序首先读取棋盘的大小n
。此时,棋盘a
和列状态标记flag
都被初始化为全0状态,表示棋盘为空,所有列都未被占用。 - 递归搜索:调用
dfs(1)
开始递归搜索。在dfs(x)
函数中,程序尝试在第x
行的每一列放置皇后。对于每一列i
,如果flag[i]
为0(表示该列未被占用)且check(x, i)
返回0(表示该位置可以放置皇后),则在该位置放置皇后(将a[x][i]
设置为1,将flag[i]
设置为1),并递归调用dfs(x+1)
来放置下一行的皇后。递归返回后,需要撤销当前位置的皇后(将a[x][i]
和flag[i]
重置为0),以便尝试其他可能性。 - 条件检查:
check()
函数是确保每一步都合法的关键。它遍历整个棋盘,检查当前位置(x, y)
是否与已放置的皇后在同一行、主对角线或副对角线上。如果有冲突,则返回1;否则返回0。 - 结果输出:当递归到达最后一行(即
x
等于n+1
)时,表示找到了一种合法的摆放方式。此时,调用pt()
函数输出当前棋盘状态,并增加计数器cnt
的值。
代码步骤讲解
全局变量定义
int a[55][55],flag[55],n,cnt=0;
a[55][55]
:二维数组,用于存储棋盘的状态,其中a[i][j]
为1表示在第i行第j列放置了一个皇后,为0表示没有放置。flag[55]
:一维数组,用于标记列是否被占用,flag[i]
为1表示第i列已被占用。n
:整数,表示棋盘的大小和皇后的数量。cnt
:整数,用于记录找到的解决方案的数量
检查函数
int check(int x,int y){
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if((i+j==x+y)||(i-j==x-y)){
if(a[i][j]==1){
return 1;
}
}
}
}
return 0;
}
这个函数用于检查在(x, y)
位置放置皇后是否合法。它遍历整个棋盘,检查当前位置是否与已放置的皇后在同一行、同一列或同一斜线上。如果发现有冲突,则返回1;否则返回0。
输出函数
void pt(){
cnt++;
cout<<cnt<<endl;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
cout<<a[i][j];
}
cout<<endl;
}
}
这个函数用于输出一个解决方案。它首先增加cnt
的值,然后输出棋盘的状态。
深度优先搜索函数
void dfs(int x){
if(x==n+1){
pt();
return ;
}
for(int i=1;i<=n;i++){
if(flag[i]==0&&check(x,i)==0){
a[x][i]=1;
flag[i]=1;
dfs(x+1);
a[x][i]=0;
flag[i]=0;
}
}
}
这是解决N皇后问题的核心函数,使用了深度优先搜索算法。它尝试在每一列放置一个皇后,并递归地处理下一行。如果成功放置了N个皇后,则调用pt()
函数输出当前方案。
代码实现
#include<bits/stdc++.h>
using namespace std;
int a[55][55],flag[55],n,cnt=0;
int check(int x,int y){
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if((i+j==x+y)||(i-j==x-y)){
if(a[i][j]==1){
return 1;
}
}
}
}
return 0;
}
void pt(){
cnt++;
cout<<cnt<<endl;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
cout<<a[i][j];
}
cout<<endl;
}
}
void dfs(int x){
if(x==n+1){
pt();
return ;
}
for(int i=1;i<=n;i++){
if(flag[i]==0&&check(x,i)==0){
a[x][i]=1;
flag[i]=1;
dfs(x+1);
a[x][i]=0;
flag[i]=0;
}
}
}
int main(){
cin>>n;
dfs(1);
return 0;
}
总结
总的来说,这篇博客对使用深度优先搜索解决特定皇后摆放问题的代码进行了详细的讲解和总结。希望读者能够从中受益,并在实际编程中灵活运用这些算法和技巧。
感谢阅读