[AcWing]BFS相关

AcWing: BFS相关

背景知识:有向图

离散数学图论中的概念,由点和边组成图,其中图中所有边都是带有方向的图称为有向图。

在算法题中,我们可以用邻接矩阵和邻接表(推荐)存储有向图。

image-20201206104559766

邻接表即类似于哈希表的拉链法,用链表来解决冲突问题。并且其中的链表顺序并不重要(存储相邻点),对于解决bfs问题来说。

宽搜框架

image-20201206113152368

例题一:图中点的层次

image-20201207183227726
#include<cstring>
#include<iostream>
#include<algorithm>

using namespace std;

const int N = 100010;
//两层节点体系:一层为idx;一层为有向图中点的编号存在e[N]中

int n, m;
int h[N], e[N], ne[N], idx; //h[N]为哈希表,e[N]用来存节点指向的节点在有向图中的编号,ne[N]为领接表上的next,idx为当前用到的节点数
int d[N], q[N]; //q[N]为队列存储bfs搜索过程,d[N]标识点是否被搜索过,记录到点1的距离

void add(int a, int b) //将边a指向b加入邻接表中
{
    e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}

int bfs()
{
    int hh = 0, tt = 0;//初始化queue队列
    q[0] = 1;
    
    memset(d, -1, sizeof d);
    
    d[1] = 0;
    
    while(hh <= tt)
    { 
        int t = q[hh ++];//bfs用队列弹出跳到下一层搜
        
        for(int i = h[t]; i != -1; i = ne[i])//bfs一般都是用for在当前层搜
        {
            int j = e[i];
            if(d[j] == -1)
            {
                d[j] = d[t] + 1;//距离加1,可以得到点1到所有点的距离,存储在数组d[N]中,也完成了这一层的搜索
                //对当前层来说t的值是固定的,所以是按层搜索,下一层t的值会变为队头的元素值,之前的队头已经出队了
                q[++tt] = j;
            }
        }
    }
    
    return d[n];
}

int main()
{
    cin >> n >> m;
    
    memset(h, -1, sizeof h);//初始化h[N]哈希表,-1代表指向为空null
    
    for(int i = 0; i < m; i ++) //存入有向图
    {
        int a, b;
        cin >> a >> b;
        add(a, b);
    }
    
    cout << bfs() << endl;
    
    return 0;
}

例题二:拓扑序列

image-20201207192400396

#include<cstring>
#include<iostream>
#include<algorithm>

using namespace std;

const int N = 100010;

int n, m;
int h[N], e[N], ne[N], idx;
int q[N], d[N];//d[N]记录有向图中编号为i的点的入度

void add(int a, int b)
{
    e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}

bool topsort()
{
    int hh = 0, tt = -1;//tt表示队列中加入的点数-1
    
    for(int i = 1; i <= n; i ++)//把入度为0的点加入队列中
        if(!d[i]) q[++tt] = i;
        
    while(hh <= tt)
    {
        int t = q[hh++];//用弹出队头来搜索下一次层
        for(int i = h[t]; i != -1; i = ne[i])//用for来进行当前层搜索
        {
            int j = e[i];
            d[j] --; //以入度减1表示该点搜过了,看习题课寻求答疑
            if(d[j] == 0) q[++tt] = j; //将入度为0的点加入队列
        }
    }
    
    return tt == n - 1;//如果为true,代表n个点全部加入了队列中
}

int main()
{
    cin >> n >> m;
    
    memset(h, -1, sizeof h);
    
    for(int i = 0; i < m; i ++)
    {
        int a, b;
        cin >> a >> b;
        add(a, b);
        d[b] ++;//顺带记录入度
    }
    
    if(topsort())
    {
        for(int i = 0; i < n; i++) printf("%d ", q[i]);
        puts("");
    }
    else puts("-1");
    
    return 0;
}

例题三、走迷宫

image-20201207205921758
#include<iostream>
#include<cstring>
#include<algorithm>

using namespace std;

typedef pair<int, int> PII;

const int N = 110;

int g[N][N];//存储地图的数组
int d[N][N];//存储距离的数组
int iuse[N][N];//存储是否用过的节点数组
int n, m;
PII q[N * N];

int bfs()
{
    int hh = 0, tt = 0;
    q[0] = {0,0};
    
    memset(d, -1, sizeof d);
    memset(iuse, -1, sizeof iuse);
    
    d[0][0] = 0;
    
    int dx[4] = {1, 0, -1, 0}, dy[4] = {0, 1, 0, -1};
    
    while(hh <= tt)
    {
        PII t = q[hh ++];//利用弹出队列来完成进行下一层搜索
        
        for(int i = 0; i < 4; i++)//利用for来完成当前层搜索
        {
            int x = t.first + dx[i], y = t.second + dy[i];
            if(g[x][y] == 0 && iuse[x][y] == -1 && x >= 0 && x < n && y >= 0 && y < m)//是否触碰地图边界?是否在障碍区域?是否被用过
            {
                iuse[x][y] = 1;
                d[x][y] = d[t.first][t.second] + 1;
                q[++ tt] = {x, y};
            }
        }
    }
    
    return d[n - 1][m - 1];
}

int main()
{
    cin >> n >> m;
    for(int i = 0; i < n; i++)
        for(int j = 0; j < m; j++)
            cin >> g[i][j];
    
    cout << bfs() << endl;
    
    return 0;
}

例题四、八数码

image-20201207212914396

//用字符串哈希表存储过程中产生的中间值,避免使用char二重数组存储带来大量的空间浪费

#include <iostream>
#include <algorithm>
#include <unordered_map>
#include <queue>

using namespace std;

int bfs(string state)
{
    queue<string> q;
    unordered_map<string, int> d;

    q.push(state);
    d[state] = 0;

    int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};

    string end = "12345678x";
    while (q.size())
    {
        auto t = q.front();
        q.pop();

        if (t == end) return d[t];

        int distance = d[t];
        int k = t.find('x');
        int x = k / 3, y = k % 3;
        for (int i = 0; i < 4; i ++ )
        {
            int a = x + dx[i], b = y + dy[i];
            if (a >= 0 && a < 3 && b >= 0 && b < 3)
            {
                swap(t[a * 3 + b], t[k]);
                if (!d.count(t))
                {
                    d[t] = distance + 1;
                    q.push(t);
                }
                swap(t[a * 3 + b], t[k]);
            }
        }
    }

    return -1;
}

int main()
{
    char s[2];

    string state;
    for (int i = 0; i < 9; i ++ )
    {
        cin >> s;
        state += *s;
    }

    cout << bfs(state) << endl;

    return 0;
}

posted @ 2020-12-07 19:48  herrhu  阅读(73)  评论(0)    收藏  举报