c++深度优先搜索经典例题之N皇后问题

题目

问题描述:

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算法的理解和应用。

代码结构

代码整体结构清晰,由全局变量定义、辅助函数、搜索函数和主函数组成。

  1. 全局变量

    • a[55][55]:二维数组,用于存储棋盘状态,a[i][j]为1表示第i行第j列放置了皇后,为0表示没有放置。
    • flag[55]:一维数组,用于标记列的状态,flag[i]为1表示第i列已被占用,为0表示未被占用。
    • n:棋盘的大小,即棋盘的行数和列数。
    • cnt:计数器,用于记录符合条件的棋盘摆放方式的数量。
  2. 辅助函数

    check(int x, int y):该函数用于检查在位置(x, y)上是否可以放置皇后。它通过遍历整个棋盘,检查当前位置是否与已放置的皇后在同一行、主对角线或副对角线上。
  3. 搜索函数

    dfs(int x):该函数是深度优先搜索的核心。它从第x行开始尝试放置皇后,通过递归调用自身来放置下一行的皇后。当x等于n+1时,表示所有行都成功放置了皇后,此时调用pt()函数输出棋盘状态。
  4. 输出函数

    pt():该函数用于打印当前棋盘状态,并增加计数器cnt的值。
  5. 主函数

    main():程序的入口点。它首先读取棋盘的大小n,然后调用dfs(1)从第一行开始递归地尝试放置皇后。
代码逻辑与实现
  1. 初始化:在main()函数中,程序首先读取棋盘的大小n。此时,棋盘a和列状态标记flag都被初始化为全0状态,表示棋盘为空,所有列都未被占用。
  2. 递归搜索:调用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),以便尝试其他可能性。
  3. 条件检查check()函数是确保每一步都合法的关键。它遍历整个棋盘,检查当前位置(x, y)是否与已放置的皇后在同一行、主对角线或副对角线上。如果有冲突,则返回1;否则返回0。
  4. 结果输出:当递归到达最后一行(即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;
}

​

总结

总的来说,这篇博客对使用深度优先搜索解决特定皇后摆放问题的代码进行了详细的讲解和总结。希望读者能够从中受益,并在实际编程中灵活运用这些算法和技巧。

感谢阅读

  • 37
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值