《算法笔记》第8章 提高篇(2)---搜索专题 8.1 深度优先搜索(DFS)+8.2广度优先搜索(BFS)

8.1深度优先搜索DFS

1.概念:

在这里插入图片描述

类似于图中走迷宫,只要遇到岔路口就继续深入下去,一直到遍历完所有的岔路口,不碰到死胡同不回头
在这里插入图片描述

2.使用递归的方式来实现深度优先搜索:

在这里插入图片描述

3.看一个具体的例子:

在这里插入图片描述

//物品编号index
//物品总重量sumW,总价值sumC
//如果不放入index号物品,则sumW不变,总价值sumC也不变
//如果index+1,则前往DFS(index+1,sumW+w[index],sumC+c[index]);
//一直到index增长到n,说明把n件物品遍历完了,此时sumw和sumc就是所选物品总质量和总价值,如果sumW不超过V,并且sumC大于最大价值记录就更新max
/*
    解题思路:
    1.DFS就只有两种思路:
        1.第一种是到达死胡同,也就是index一直遍历到n了
        2.第二种是遇到岔路口,就只有两种选择:一种是进行下一件产品并不改变总价值和总重量,另一中是进行到下一个index物品,并且改变总重量和总价值
    2.main:
        1.输入n件物品和对应的价值
        2.初始化DFS全为0
        3.最后输出最大值即可
*/
#include<iostream>
#include<cstdio>
using namespace std;
const int maxn=30;
int v,maxvalue=0,n;
int w[maxn],c[maxn];
void DFS(int index,int sumW, int sumC)
{
    if(index==n)
    {
        if(sumW<=v && sumC>=maxvalue)
        {
            maxvalue=sumC;

        }
        return ;        //注意return的位置
    }

    DFS(index+1,sumW,sumC);
    DFS(index+1,sumW+w[index],sumC+c[index]);
}
int main()
{

    cin >> n >> v;
    for(int i=0; i<n; i++)
        cin >> w[i];
    for(int i=0; i<n; i++)
        cin >> c[i];
    DFS(0,0,0);
    cout << maxvalue;
}

在这里插入图片描述

8.2广度优先搜索(BFS)

概念:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

下面这张图是对上面的总结:

在这里插入图片描述

BFS的模板

void BFS(int s)
{
	queue<int > q;
	q.push(s);
	while(!q.empty())
	{
		取出队首元素top
		访问队首元素top
		将队首元素出队
		将top的下一层节点中未曾入队的结点全部入队,并设置为已入队
	}
}

例子1:

在这里插入图片描述

下张图是对上面例题的解释

在这里插入图片描述


#include<iostream>
#include<queue>
using namespace std;

const int maxn=100;
struct node
{
    int x,y;
}Node;

int matrix[maxn][maxn];
bool inq[maxn][maxn]={false};  //inq中表示为true的点其实都表示的是某一块中的点,并且所有能入队列中的点也都是这一块中的点
int X[4]={0,0,1,-1};
int Y[4]={1,-1,0,0};
int n,m;

bool judge(int x, int y)
{
    if(x<0 || x>=n || y<0 || y>=m)
        return false;
    if(matrix[x][y]==0 || inq[x][y]==true)    //matrix中有俩个值,如果是0肯定不用管,如果inq为true表示这个点已经计算过
        return false;
    return true;
}

//设置结点结构node,结构元素为(x,y)
//矩阵大小为n*m,matrix矩阵为输入的01矩阵,inq记录位置x,y是否已经入过队列
//两个增量数组X,Y分别表示上下左右:(x+1,y)(x-1,y)(x,y-1)(x,y+1)
//判断(x,y)是否需要访问,如果超出边界则返回false,如果元素值为0或者已经如果入过队列也返回false,以上都不满足则返回true
/*
    BFS访问其所在的块,函数参数是x,y
    设置一个结构队列:Q,将元素的位置x,y放到队列中
    按照BFS模板:
        队列Q不为空
        取出队首元素
        队首元素出队
        循环4次:访问该结点的上下左右4个位置,并用judge进行判断;如果满足条件重新设置一个结点的位置,把合适的点放到队列中,并且将对应的位置的值标记为true
    BFS的作用:将某一个点的上下左右位置的点如果是1则全部标记为true,同时

*/
/*
    main函数:
    1.输入n,m
    2.读入矩阵
    3.设置块数
    4.双层for循环读入枚举每一个位置,如果元素值为1并且数字为false,则块数加1并且BFS访问整个块
*/

void BFS(int x, int y)          //BFS只能访问某一块的点
{
    queue<node> Q;
    Node.x=x;
    Node.y=y;
    Q.push(Node);
    inq[x][y]=true;
    while(!Q.empty())
    {
        node q=Q.front();
        Q.pop();
        for(int i=0; i<4; i++)   //上下左右4个点
        {
            int newX=q.x+X[i];    //注意这一定是队列中第1个结点的x和y
            int newY=q.y+Y[i];
            if(judge(newX,newY))
            {
                Node.x=newX;  //这个Node还是之前的
                Node.y=newY;
                Q.push(Node);
                inq[newX][newY]=true;
            }
        }
    }
}
int main()
{
    cin >> n >> m;
    for(int i=0; i<n; i++)
        for(int j=0; j<m; j++)
            cin >> matrix[i][j];
    int ans=0;
    for(int i=0; i<n; i++)
        for(int j=0; j<m; j++)
        {
            if(matrix[i][j]==1 && inq[i][j]==false)   //使用两个数组matrix和inq,其中matrix表示数字01,inq表示true或者false
            {
                ans++;
                BFS(i,j);   //遍历了每一块的点
            }
        }
    cout << ans;
    return 0;
}


列子2:

在这里插入图片描述

#include<iostream>
#include<queue>
using namespace std;
const int maxn=100;
struct node
{
    int x,y,step;
}S,T,Node;


char matrix[maxn][maxn];
bool inq[maxn][maxn]={false};   //注意inq表示元素是否进入过队列,而非是否已经访问过,区别在于:如果设置为是否已经访问过时,就会重复计算点

int X[4]={0,0,1,-1};
int Y[4]={1,-1,0,0};
int n,m;

bool judge(int x, int y)
{
    if(x<0 || x>=n || y<0 || y>=m)
        return false;
    if(matrix[x][y]=='*')  //如果遇到*,表示遇到墙壁则返回false
        return false;
    if(inq[x][y]==true)
        return false;
    return true;
}

int BFS()         //不要参数
{
    queue<node> Q;
    Q.push(S);
    while(!Q.empty())
    {
        node top=Q.front();
        Q.pop();               //每次从队列中拿到一个值,肯定要弹出一个值
        if(top.x==T.x && top.y==T.y)      //如果队首元素对应的值是终点的所以直接返回相关的长度
        {
            return top.step;
        }
        for(int i=0; i<4; i++)
        {
            int newX=top.x+X[i];
            int newY=top.y+Y[i];
            if(judge(newX,newY))
            {
                Node.x=newX;
                Node.y=newY;
                Node.step=top.step+1;
                Q.push(Node);
                inq[newX][newY]=true;
            }
        }
    }
    return -1;
}

int main()
{

    cin >> n >> m;
    for(int i=0; i<n; i++)
    {
        getchar();
        for(int j=0; j<m; j++)
        {
            cin >> matrix[i][j];   //注意将字符输入进去
        }
        matrix[i][m+1]='\0';
    }

    cin >> S.x >> S.y >> T.x >> T.y;
    S.step=0;
    printf("%d",BFS());
    return 0;
}



将结构数组放到对列中的注意事项:

在这里插入图片描述

#include<iostream>
#include<queue>
using namespace std;
/*
    解题思路:
    1.用STL中对列时,将结构数组放到队列中,改变队列中的值不会改变数组结构的值
    2.同样改变结构数组中的值不会改变队列中的值
    3.队列相当于创建了该结构数组的一个副本
*/
struct node
{
    int x;
}a[10];
int main()
{
    queue<node> q;
    for(int i=1; i<=3; i++)
    {
        a[i].x=i;
        q.push(a[i]);    //将结构数组放到队列中
    }
    q.front().x=1000;   //改变队列中的值不会改变结构数组中的值
    cout << a[1].x << " " << a[2].x << " " << a[3].x << endl;
    a[1].x=200;       //同样改变结构数组中的值不会改变队列中的值
    cout << q.front().x;
    return 0;

}

所以说如果想通过队列改变值的话,最好在队列中存放元素的下标

#include<iostream>
#include<queue>
using namespace std;
/*
    解题思路:
    1.用STL中对列时,将结构数组放到队列中,改变队列中的值不会改变数组结构的值
    2.同样改变结构数组中的值不会改变队列中的值
    3.队列相当于创建了该结构数组的一个副本
*/
struct node
{
    int x;
}a[10];
int main()
{
    queue<int> q;
    for(int i=1; i<=3; i++)
    {
        a[i].x=i;
        q.push(i);    //将结构数组放到队列中
    }
    a[q.front()].x=100;  //这个相当于直接改变结构数组的值
    cout << a[1].x << " " << a[2].x << " " << a[3].x;
    return 0;

}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值