DFS(深度优先搜索)、BFS(广度优先搜索)

DFS 

dfs是一种思想,并不是一种固定的算法,它不仅仅只在图论的问题中出现。有些时候,一些非图论的题的问题也可以转化成dfs问题。要掌握dfs必须见许多的题。这里只以最简单的题目为例,阐述dfs的思想,以及给出例题的题解。

dfs的思想是什么?

dfs思想的重点在于回溯,与递归类似。它会先将某一条路走到穷尽,然后换另一条路走,当某一个节点的方向全部走完后,回溯到上一个节点,重复上述过程,直到满足条件或者穷尽所有可能的路径。相信初学者根本没看懂这到底说的什么意思。dfs用文字描述起来特别抽象,所以需要例题和图示。

提醒一下,下面将要讲的例子只不过是一种很简单的dfs,弄懂了例题并不代表弄懂了dfs。只能说是学会dfs的基本思想。想要深入弄懂dfs,必须见各种题,慢慢来吧,实际上我现在遇到的dfs题也远远不够,dfs需要长期实践才能彻底参透。

直接以例题为切入点开始。

例题-棋盘问题

原本是想用八皇后作为例题,但是hduoj不对外开放了,所以换成了棋盘问题,这两个例题都是典型的dfs例题。

 题目连接: 1321 -- 棋盘问题 (poj.org)

题目描述:

 题目大意:. 的地方不能放棋子,#的地方必须保证同行同列只能有一个棋子,问摆放k个棋子的方案个数。

例如:假设现有以下3*3棋盘:

DFS(深度优先搜索)、BFS(广度优先搜索)_第1张图片

要求放入3个棋子,问有几种方案?

对于这道题,每个格子只有放或者不放两种可能。所以一次回溯即回到该格子没有放置棋子的状态。然后在该状态下寻找其他满足条件的格子放置棋子。如果这一行上所有满足条件的格子都放置过了,则再次回溯到上一行。重复此过程。

简单做了这个例子完整dfs的流程gif。

DFS(深度优先搜索)、BFS(广度优先搜索)_第2张图片

 这是这道题的dfs完整版。但是在写代码的时候有很多可以简化的地方。

比如,每一行只能放一个,所以我们只需要一个一维数组来保存列即可。每一次dfs代表进入下一行,一个全局一维数组代表每一列的状态,这样就在计算时将二维数组压缩成了一维数组。(当然最开始存棋盘还是得用二维数组存)

整个详细过程和细节写在代码里了。这里直接上AC代码:

#include
#include
#include
#include
#include
using namespace std;
typedef long long int ll;
const int MAX_N = 11;
char map[MAX_N][MAX_N];//存放最初的棋盘
int vis[MAX_N];//列状态,每个下标代表一列,0代表该列无棋子,1代表有棋子
int cnt;//棋盘上的棋子数量
ll ans;//最终答案,即方案数
int n,m;//n为地图大小,m为需要放置棋子个数

//初始化函数
void init(){
    cnt = 0;
    ans = 0;
    memset(map,0,sizeof(map));
    memset(vis,0,sizeof(vis));
}

void dfs(int now)//now代表所在第几行
{
    if(cnt == m){//如果棋子全部放置完毕
        ans ++;//方案数加一
        return;//返回去准备回溯
    }
    if(now > n){//如果没有放置完棋子并且已经没有行可以放棋子了
        return;//返回去准备回溯
    }
    dfs(now+1);//不填该行,直接进入下一行
    for(int i = 1;i<=n;i++){//扫描列
        if(map[now][i] == '#'&&vis[i] == 0){//如果有位置可以填
            cnt++;//放一颗棋子
            vis[i] = 1;//该列状态置为1,即该列有棋子了
            dfs(now+1);//下一行
            vis[i] = 0;//回溯
            cnt--;
        }
    }
}

int main()
{
    char c;//c是用来吞/n的
    while(scanf("%d%d",&n,&m) && n!=-1&&m!=-1){
        c = getchar();
        init();//记得初始化
        for(int i = 1;i<=n;i++){
            for(int j = 1;j<=n;j++){
                scanf("%c",&map[i][j]);
            }
            c = getchar();
        }
        dfs(1);//参数表示所在行,第一行序号为1
        printf("%lld\n",ans);
    }
    return 0;
}

BFS

BFS,广度优先算法,以源点为圆心,向周围一层层扩散开去的搜索算法。也可以形象的将它比作为正在扩散的水波。该算法运用了队列。本身不难。

前置知识

队列:队列是一种数据结构,特点为先入先出,就像排队时一样,先排队的人先离开,后来的人后离开。这里不会讲队列的实现原理。代码中均使用C++ stl的queue。可以自行搜索使用方法。

优先队列:BFS有时会与优先队列联合使用。这里暂时不提,但是到最短路时会有一个Dijkstra堆优化,这里贴一个之前写的优先队列的连接,本篇中不作要求。

 c++ STL 优先队列_issey的博客-CSDN博客

例题

 与之前写的DFS一样,通过简单的例题说明BFS的思路和使用方法。

题目连接:1979 -- Red and Black (poj.org)

题目描述:

题目大意:有一个人站在黑块的位置,可以上下左右移动,但是白块不能踩,问能踩到的最大黑块数目是多少。输入地图时,.为黑块,#为白块,@为这个人起始位置。(注意@也是黑块)。


思路:典型的BFS题,现在以@为圆心,假想以传波的形式遍历地图,其中#会阻挡波,看能走多少个 . 。(注意答案还要加上起点本身)  

思路很简单,问题是我们要怎么模拟这个所谓的“波”。队列的特点在于先进先出,而波的扩散是从里到外。在这道题中,小人只能以上下左右的方向行动,所以先依次将距离源点最近的上下左右中满足条件(即 . )的四个点放入队列尾部。然后读出队列头部(最先加入队列的点),再以该点为圆心,将距离最近且没有访问过的点放入队列尾部。循环上述过程,直到队列为空,结束计算。

假设现在地图如下:

DFS(深度优先搜索)、BFS(广度优先搜索)_第3张图片

 以黑格点为起点,遍历整张地图。

DFS(深度优先搜索)、BFS(广度优先搜索)_第4张图片

 

这是每一轮的队列流程图,一个个对照着地图看一看应该就能懂。

再在代码里加上#以及是否越界的判定条件即可。对于BFS的思想写了这道题就差不多明白了。

AC代码:

#include
#include
#include
#include
#include
using namespace std;
const int MAX_SIZE = 30; 
typedef struct Node{
	int x,y;
}Node;
int Cnt;//能走的方块个数 
int vis[MAX_SIZE][MAX_SIZE];//是否已经访问过 
char map[MAX_SIZE][MAX_SIZE];//存地图
int X[4] = {0,-1,1,0};
int Y[4] = {1,0,0,-1};//方向

//判断某一点坐标在不在地图内
bool judge(int x,int y,int n,int m)
{

	if(x>=0&&x=0&&y Q)
{
    printf("当前队列:");
    while(!Q.empty()){
        printf("(%d,%d)     ",Q.front().x,Q.front().y);
        Q.pop();
    }
    printf("\n");
}


int main()
{
	int n,m;
	int i,j;

	while(scanf("%d %d",&m,&n)&&n!=0&&m!=0)
	{
		Node now,next,start;
		queue Q; //队列
		memset(map,0,sizeof(map));
		memset(vis,0,sizeof(vis));
		Cnt = 0;
		for(i = 0;i

 

你可能感兴趣的:(图论,ACM学习,深度优先,图论,算法)