【学习笔记】广度优先搜索和深度优先搜索(BFS和DFS)

广度优选搜索(BFS)

BFS大致思路:
1.首先创建一个visit[ ]数组和一个队列q,分别用来判断该位置是否已经访问过及让未访问过的点入队;
2.初始化visit[ ]数组,清空q队列;
3.让起点start入队,并使该点的visit置1;
4.while(!q.empty()){…}执行搜索操作,

  • a.取出队头元素后使队头元素出队,判断该元素是否为目标到达点;

  • b.如果是目标点,就返回结果(一般是最短时间、最短路径);

  • c.如果不是目标点,就继续访问与其相邻的位置点,将可走的相邻的位置点 入队,并更新visit[ ]数组;

这里放一道来自牛客的例题
例题的AC代码:

#include <bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<b;i++)	
#define T int t ;cin >> t;while(t--)
using namespace std ;
typedef long long ll;
typedef unsigned long long ull;
const int maxn = 2e5 + 10;
const int INF = 0x3f3f3f3f;
const double eps = 1e-11;
const ll mod = 1e9 + 7;
char mp[40][40];
int coutt[4][2]= {{1,0},{-1,0},{0,1},{0,-1}};
int si,sj;
int ei,ej;
int n,m;
int minn=INF;
int dis[40][40];
int a[40][40];  
struct node{
	int x,y ;
	
};
inline bool check(int x,int y)
{
    return x>=1&&x<=n&&y>=1&&y<=m;
}
void bfs()  //核心代码
{
	queue<node>q ;
	memset(dis,INF,sizeof(dis));      
	q.push(node{si,sj}) ;  //将起点压缩进入队列 
	dis[si][sj]=0;  //dis数组表示起点到目标位置的距离 
	while(!q.empty())
	{
		node xx = q.front() ; //xx是指的上一个位置 
		q.pop() ; //将第一个位置弹出 
		if(xx.x==ei&&xx.y==ej)  //如果到达目的地 
        {
            minn=min(minn,dis[xx.x][xx.y]);  // 选出到达终点的最优解 
            continue;
        }
        for(int i=0; i<4; i++)     //查看四处方向 
        {
            int t1=xx.x+coutt[i][0];
            int t2=xx.y+coutt[i][1];
            if(check(t1,t2))  //确认下一步是否在方阵之中 
            {
                if(dis[t1][t2]>dis[xx.x][xx.y]+a[t1][t2])
                {
                    dis[t1][t2]=dis[xx.x][xx.y]+a[t1][t2];
                    q.push(node{t1,t2}); //将下一个点放入队列 
                }
            }
        }
	}
}
int main()
{
	scanf("%d %d ",&n,&m);
    for(int i=1; i<=n; i++)
    {
        for(int j=1; j<=m; j++)
        {
            scanf(" %c",&mp[i][j]);
            if(mp[i][j]=='S')  //标记起点
            {
                si=i;
                sj=j;
                continue;
            }
            if(mp[i][j]=='E')  /标记终点
            {
                ei=i;
                ej=j;
                continue;
            }
            if(mp[i][j]=='A'||mp[i][j]=='B'||mp[i][j]=='C')
            {
                a[i][j]=100;
            }
            else
                a[i][j]=mp[i][j]-'0';
        }
    }

    bfs();
    printf("%d\n",minn);
    return 0;
 } 

下面,我再整理一个bfs的变式的例题:
洛谷:P1443 马的遍历
这是一道裸的bfs模板题目,也是用队列写的,简单易懂。

#include <bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<b;i++)	
#define T int t ;cin >> t;while(t--)
using namespace std ;
typedef long long ll;
typedef unsigned long long ull;
const int maxn = 405;
ll n  , m , x, y ;
ll dis[maxn][maxn] ;
ll vis[maxn][maxn] ;
ll xx[8] = {2,2,1,1,-2,-2,-1,-1} ;  //注意,马的走路的方式不同于其他题目,马是八面玲珑
ll yy[8] = {1,-1,2,-2,1,-1,2,-2} ;  //所以有八个方向
inline bool check(ll a ,ll b){  //确保下一个点在期盼的范围内
	return a>0&&a<=n&&b>0&&b<=m ;
}
struct node{
	ll x,y ,path;
};
void bfs(ll x , ll y){
	dis[x][y] = 0 ;  //起点到起点的长度为0
	queue<node> q ;
	q.push(node{x,y,0}) ; //将起点压入队列中
	while(!q.empty()){
		node tmp = q.front() ;
		q.pop() ;
		for(int i = 0 ; i < 8 ; i++){
			ll aa = tmp.x +xx[i] ;
			ll bb = tmp.y +yy[i] ;
			if(check(aa,bb)&&dis[aa][bb]==-1){
				dis[aa][bb] = tmp.path + 1 ;
				q.push(node{aa,bb,dis[aa][bb]}) ;
			}
		}
	}
} 
int main()
{
	cin >> n >> m >>  x >>  y ;;
	memset(dis,-1,sizeof(dis)) ;
	bfs(x, y) ;
	for(int i = 1 ; i <= n ; i++){
		for(int j = 1 ; j <= m ;j++){
			printf("%-5lld",dis[i][j]) ; //左对齐,宽5格 这里被卡了一次...
		}
		cout << endl ;
	}
	return 0 ;
} 

广度优先搜索(DFS)

DFS(Depth First Search)
算法竞赛中的一个重要技巧,在许多题目里,用DFS有着神奇的作用。
dfs就是一种十分暴力的方法,枚举所有的情况进行暴力搜索
常用题目类型:迷宫 , 选数,捉迷藏等等。

洛谷:P1036 选数

#include <bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<b;i++)
#define T int t ;cin >> t;while(t--)
using namespace std ;
typedef long long ll;
typedef unsigned long long ull;
const int maxn = 2e5 + 10;
const int INF = 0x3f3f3f3f;
const double eps = 1e-11;
const ll mod = 1e9 + 7 ;
ll n , k ,sum,ans = 0 ;
ll a[maxn] ;
int cnt ;
bool isprime(int n) {  //判断素数的模板
	int s=sqrt(double(n));
	for(int i=2; i<=s; i++) {
		if(n%i==0)return false;
	}
	return true;
}
void dfs(int x) { // 很常规的dfs模板
	if(cnt == k) {
		if(isprime(sum)) {
			ans++ ;
		}

	} else {
		for(int i = x +1; i < n ; i++) {
			cnt++ ;
			sum +=a[i] ;
			dfs(i) ;
			cnt-- ;
			sum -=a[i] ;
		}
	}
	return ;
}
int main() {
	cin >> n >> k ;
	for(int i = 0 ; i < n ; i++) cin >>a[i] ;
	sum = 0 ;
	cnt = 0 ;
	for(int i = 0 ; i < n ; i++) {
		sum = a[i] ; cnt = 1 ;
		dfs(i) ;
	}
	printf("%lld\n",ans) ;
	return 0 ;
}

例题:

  • 【poj】迷宫问题
    由于POJ不能使用万能头文件,在某些地方写的会相对复杂一点…这道例题我用的是BFS,由于数据量比较小,也可以用BFS完成
    下面是AC代码:
#include<iostream>   //不知道什么原因万能头文件好像不能用
#include<algorithm>
#include<queue>
#include<stack>
#include <cstring>
#define rep(i,a,b) for(int i=a;i<b;i++)
#define T int t ;cin >> t;while(t--)
using namespace std ;
typedef long long ll;
typedef unsigned long long ull;
const int maxn = 10;
const int INF = 0x3f3f3f3f;
const double eps = 1e-11;
const ll mod = 1e9 + 7;
ll n  = 5 , m ;
ll ans[maxn] ;
int a[maxn][maxn] ;
inline bool check(int x,int y) {
	return x>=0&&x<n&&y>=0&&y<n ;
}
int xa[4] = {1,0,0,-1} ;
int xb[4] = {0,1,-1,0} ;
struct node {
	int x , y ;
	node(int x,int y):x(x),y(y)
	{}
};
struct road {
	int x , y ;
	road(){
	}
	road(int x,int y):x(x),y(y)	{}
} dis[maxn][maxn];   //存储走过的路径

int vis[maxn][maxn];
void bfs() {
	queue<node> q ;
	while(!q.empty()) q.pop() ;
	q.push(node(0,0)) ;
	while(!q.empty()) {
		node tmp = q.front() ;
		q.pop() ;
		for(int i = 0 ; i < 4 ; i++) {
			int xx = tmp.x + xa[i] ;
			int yy = tmp.y + xb[i] ;
			if(check(xx,yy)&&!a[xx][yy]&&!vis[xx][yy]) {
				q.push(node(xx,yy)) ;
				dis[xx][yy].x = tmp.x ;
				dis[xx][yy].y = tmp.y;
				vis[xx][yy] = 1 ;
			}
		}
	}
}
int main() {

	memset(vis,0,sizeof vis) ;
	dis[0][0].x = 0 ;
	dis[0][0].y = 0 ;
	for(int i = 0 ; i < n ; i++)
		for(int j = 0 ; j < n ; j++)
			cin >> a[i][j] ;
	bfs() ;
	int xx , yy ;
	xx = yy = 4 ;
	stack<road> s ;  //利用栈先进后出的特点,把路径压入栈中 
	while(xx != 0 || yy != 0) {
		int x , y ;
		x= dis[xx][yy].x ;
		y =dis[xx][yy].y ;
		s.push(road (xx,yy)) ;
		xx =  x ; yy = y;
		
	} 
	s.push(road (0,0)) ;
	while(!s.empty()) {
		road tmp = s.top() ;
		cout <<"(" << tmp.x <<", "<<tmp.y <<")"<< endl ;
		s.pop() ;
	}
	return 0 ;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值