BFS 广度优先搜索 ( Breadth-First-Search )

原题单链接:https://www.luogu.com.cn/paste/sa0zary9 || https://rentry.org/3r68rga7

广度优先搜索算法(Breadth First Search),又称为”宽度优先搜索”, BFS是用于图的查找算法(要求能用图表示出问题的关联性)。

BFS可用于解决2类问题:

1.从A出发是否存在到达B的路径;DFS也可求

2.从A出发到达B的最短路径;数据小20以内的话, DFS也不是不可以 题眼

整体思路

其思路为从图上一个节点出发,访问先访问其直接相连的子节点,若子节点不符合,再问其子节点的子节点,按级别顺序(一层一层)依次访问,直到访问到目标节点。

步骤

  • 起始:将起点(源点,树的根节点)放入队列中
  • 扩散:从队列中取出队头的结点,将它的相邻结点放入队列,不断重复这一步
  • 终止:当队列为空时,说明我们遍历了所有的能到的结点,整个图能到的点都被搜索了一遍

时空复杂度

时间复杂度 : 最差情形下,BFS必须寻找所有到可能节点的所有路径,因此其时间复杂度为O(|V| + |E|),其中|V|是节点的数目,而|E|是图中边的数目。

空间复杂度 : BFS的空间复杂度为 O(B^h),其中B是最大分支系数,而h是树的最长路径长度(树的高度) 。由于对空间的大量需求,因此BFS并不适合解非常大的问题。

对于所有边长度相同的情况,比如地图的模型,bfs第一次遇到目标点,此时就一定是从根节点到目标节点最短的路径(因为每一次所有点都是向外扩张一步,你先遇到,那你就一定最短)。bfs找到的一定是最短的。但是如果是加权边的话这样就会出问题了,bfs传回的是经过边数最少的解,但是因为加权了,这个解到根节点的距离不一定最短。比如1000+1000是只有两段,1+1+1+1有4段,由于bfs返回的经过边数最少的解,这里会返回总长度2000的那个解,显然不是距离最短的路径。此时我们就应该采用Dijkstra最短路算法解决加权路径的最短路了。

SPFA

例题

acw844. 走迷宫 ,

#include <bits/stdc++.h>

#define x first
#define y second

using namespace std;

const int N = 110;
typedef pair<int,int> pii;

int n,m;
int g[N][N];
int dist[N][N];
queue<pii> q;

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

int bfs(int sx,int sy)
{
    memset(dist,-1,sizeof dist); // -1 means not visited
    q.push({sx,sy});
    dist[sx][sy]=0; // start point

    while (q.size()&&!q.empty()) // while queue is not empty
    {
        auto t=q.front();
        q.pop();

        for (int i = 0; i < 4; i++)
        {
            int a=t.x+dx[i],b=t.y+dy[i];

            if(a<1||a>n||b<1||b>m) continue; // out of bound
            if(g[a][b]!=0) continue; // if there is a wall
            if(dist[a][b]>0) continue; // if already visited

            q.push({a,b}); // push the next point
            dist[a][b]=dist[t.x][t.y]+1;

            if(a==n&&b==m)
            {
                return dist[a][b];
            }
        }
    }
    return dist[n][m];
}

int main()
{
    cin >> n >> m;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            cin >> g[i][j];
        }
    }
    int res=bfs(1,1);
    cout << res;
}

若打不开这个题就看这个 P1746 离开中山路

代码见下文。


习题课

热身 – 简单迷宫问题

P1746 离开中山路 1

#include <bits/stdc++.h>

using namespace std;

typedef pair<int,int> pii;

const int N=1010;
int n;
int mx1,my1,mx2,my2;
char g[N][N];
int dist[N][N];
int res;
queue<pii> q;

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

int bfs(int x,int y)
{
    memset(dist,-1,sizeof(dist));
    q.push({x,y});
    dist[x][y]=0;

    while (!q.empty())
    {
        auto t=q.front();
        q.pop();

        for(int i=0;i<4;i++)
        {
            int a=t.first+dx[i],b=t.second+dy[i];
            if(a<1||a>n||b<1||b>n)continue;
            if(g[a][b]!='0')continue;
            if(dist[a][b]>=0)continue;

            q.push({a,b});
            dist[a][b]=dist[t.first][t.second]+1;

            if(a==mx2&&b==my2)
            {
                return dist[a][b];
            }
        }
    }
    return dist[mx2][my2];
}

int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            cin>>g[i][j];
        }
    }
    // for(int i=1;i<=n;i++)
    // {
    //     cin>>g[i]+1;
    // }
    cin>>mx1>>my1;
    cin>>mx2>>my2;
    res=bfs(mx1,my1);
    cout<<res<<'\n';
    return 0;
}

P1443 马的遍历 1

#include <bits/stdc++.h>

using namespace std;
typedef pair<int,int> pii;

const int N=410;
int n,m,x,y;
int dist[N][N];
pii q[N*N];

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

void bfs(int x,int y)
{
    memset(dist,-1,sizeof(dist));
    q[0]={x,y};
    dist[x][y]=0;
    int hh=0,tt=0;

    while(hh<=tt)
    {
        auto t=q[hh++];
        for(int i=0;i<8;i++)
        {
            int a=t.first+dx[i],b=t.second+dy[i];

            if(a<1||a>n||b<1||b>m)continue;
            if(dist[a][b]>=0)continue;

            dist[a][b]=dist[t.first][t.second]+1;
            q[++tt]={a,b};
        }
    }
}

int main()
{    
    scanf("%d %d %d %d",&n,&m,&x,&y);
    bfs(x,y);
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            printf("%-5d",dist[i][j]);
        }
        printf("\n");
    }
}

————–P1747 好奇怪的游戏 (跑2次) 0

————–P2385 Bronze Lilypad Pond B 可控马步

多源BFS

P1332 血色先锋队 1

#include <bits/stdc++.h>

using namespace std;

typedef pair<int,int> pii;

const int N=510;
int n,m,a,b;
int dist[N][N];
pii q[N*N];

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

void bfs()
{
    while(hh<=tt){
        auto t=q[hh++];

        for(int i=0;i<4;i++)
        {
            int a=t.first+dx[i],b=t.second+dy[i];
            if(a<1||a>n||b<1||b>m)continue;
            if(dist[a][b]>=0)continue;

            dist[a][b]=dist[t.first][t.second]+1;
            q[++tt]={a,b};
        }
    }
}

int main()
{    
    cin>>n>>m>>a>>b;
    memset(dist,-1,sizeof(dist));
    while(a--)
    {
        int x,y;
        cin>>x>>y;
        q[++tt]={x,y};
        dist[x][y]=0;
    }
    bfs();
    while(b--)
    {
        int x,y;
        cin>>x>>y;
        cout<<dist[x][y]<<'\n';
    }
    return 0;
}

——————–acw173.矩阵距离 0

染色问题

P1162 填涂颜色9 1

#include <bits/stdc++.h>

using namespace std;

typedef pair<int,int> pii;

const int N=40;
int n;
int g[N][N];
bool st[N][N];
pii q[N*N];

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

void bfs(int x,int y)
{
    q[0]={x,y};
    st[x][y]=true;
    int hh=0,tt=0;

    while(hh<=tt)
    {
        auto t=q[hh++];

        for(int i=0;i<4;i++)
        {
            int a=t.first+dx[i],b=t.second+dy[i];

            if(a<0||a>n+1||b<0||b>n+1)continue;
            if(st[a][b])continue;
            if(g[a][b]==1)continue;

            st[a][b]=true;
            q[++tt]={a,b};
        }
    }

    return;
}

int main()
{    
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            cin>>g[i][j];
        }
    }
    bfs(0,0);
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            if(g[i][j]==0&&!st[i][j])g[i][j]=2;
            cout<<g[i][j]<<' ';
        }
        cout<<'\n';
    }
    return 0;
}

———————–P1506 拯救oibh总部

———————–CF1059B Forgery

有外界干扰的迷宫问题

P2895 Meteor Shower S 天降陨石 1

#include <bits/stdc++.h>

using namespace std;

typedef pair<int,int> pii;

const int N=310;

int m;
int dist[N][N];
int fire[N][N];
pii q[N*N];

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

int bfs()
{
    q[0]={0,0};
    dist[0][0]=0;
    int hh=0,tt=0;

    while(hh<=tt)
    {
        auto t=q[hh++];

        for(int i=0;i<4;i++)
        {
            int a=t.first+dx[i],b=t.second+dy[i];

            if(a<0||b<0)continue; // 超出边界
            if(dist[a][b]>=0)continue; // 已访问过
            if(dist[t.first][t.second]+1>=fire[a][b])continue; // 预先检查,如果被砸死了就不用管这个点了

            dist[a][b]=dist[t.first][t.second]+1;
            q[++tt]={a,b};

            if(fire[a][b]==0x3f3f3f3f)return dist[a][b];
        }
    }
    return -1;
}

int main()
{    
    cin>>m;
    memset(fire,0x3f,sizeof(fire));
    memset(dist,-1,sizeof(dist));
    while (m--)
    {
        int x,y,t;
        cin>>x>>y>>t;
        fire[x][y]=min(t,fire[x][y]);
        for(int i=0;i<4;i++)
        {
            int a=x+dx[i],b=y+dy[i];
            if(a<0||a>301||b<0||b>301)continue;
            fire[a][b]=min(t,fire[a][b]);
        }
    }
    int res=bfs();
    cout<<res<<'\n';
    return 0;
}

——————–P3395 路障 天降路障 0.5

看起来比较困难的BFS

P2658 汽车拉力比赛 1

#include <bits/stdc++.h>

using namespace std;

typedef pair<int, int> pii;

const int N = 510;
int m, n;
int high[N][N];
int flag[N][N];
int cnt_flag = 0;
pii q[N * N];
int x = 0, y = 0;
bool st[N][N];

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

bool check(int mid)
{
    q[0] = {x, y};
    st[x][y]=true;
    int cnt = 1;
    int hh = 0, tt = 0;

    while (hh <= tt)
    {
        auto t = q[hh++];

        for (int i = 0; i < 4; i++)
        {
            int a = t.first + dx[i], b = t.second + dy[i];

            if (a < 1 || a > m || b < 1 || b > n)
                continue;
            if (st[a][b])
                continue;
            if (abs(high[t.first][t.second] - high[a][b]) > mid)
                continue;

            q[++tt] = {a, b};
            st[a][b] = true;

            if (flag[a][b] == 1)
            {
                cnt++;
                if (cnt == cnt_flag)
                {
                    return true;
                }
            }
        }
    }
    return false;
}

int main()
{
    cin >> m >> n;
    for (int i = 1; i <= m; i++)
    {
        for (int j = 1; j <= n; j++)
        {
            cin >> high[i][j];
        }
    }
    for (int i = 1; i <= m; i++)
    {
        for (int j = 1; j <= n; j++)
        {
            cin >> flag[i][j];
            if (flag[i][j] == 1)
            {
                cnt_flag++;
            }
        }
    }
    for (int i = 1; i <= m; i++)
    {
        for (int j = 1; j <= n; j++)
        {
            if (flag[i][j] == 1)
            {
                x = i, y = j;
                i = m + 1;
                break;
            }
        }
    }

    int l = -1, r = 1e9 + 10;
    while (l + 1 < r)
    {
        int mid = (l + r) / 2;
        memset(q, 0, sizeof(q));
        memset(st, false, sizeof(st));
        if (check(mid))
        {
            r = mid;
        }
        else
            l = mid;
    }
    cout << r;
    return 0;
}

————–P1126 机器人搬重物

P2730 魔板 Magic Squares

#include <bits/stdc++.h>

using namespace std;

int g[2][4];
unordered_map<string, int> dist;
unordered_map<string, pair<char, string>> pre;
queue<string> q;

void set1(string state)
{
    for (int i = 0; i < 4; i++)
    {
        g[0][i] = state[i];
    }
    for (int i = 3, j = 4; i >= 0; i--, j++)
    {
        g[1][i] = state[j];
    }
}

string get1()
{
    string res;
    for (int i = 0; i < 4; i++)
    {
        res += g[0][i];
    }
    for (int i = 3; i >= 0; i--)
    {
        res += g[1][i];
    }
    return res;
}

// 交换上下两行,A
string move0(string state)
{
    set1(state);
    for (int i = 0; i < 4; i++)
    {
        swap(g[0][i], g[1][i]);
    }
    return get1();
}

// 将最右边的一列移动到最左边,B
string move1(string state)
{
    set1(state);

    char v0 = g[0][3], v1 = g[1][3];
    for (int i = 3; i > 0; i--)
    {
        g[0][i] = g[0][i - 1];
        g[1][i] = g[1][i - 1];
    }
    g[0][0] = v0, g[1][0] = v1;
    return get1();
}

// 中央四个元素顺时针旋转,C
string move2(string state)
{
    set1(state);
    char v0 = g[0][1], v1 = g[0][2], v2 = g[1][2], v3 = g[1][1];
    g[0][1] = v3, g[0][2] = v0, g[1][2] = v1, g[1][1] = v2;
    return get1();
}

int bfs(string start, string end)
{
    q.push(start);
    dist[start] = 0;
    while (q.size())
    {
        auto t = q.front();
        q.pop();

        if (t == end)
        {
            return dist[t];
        }

        string m[3];
        m[0] = move0(t), m[1] = move1(t), m[2] = move2(t);
        for (int i = 0; i < 3; i++)
        {
            if (!dist.count(m[i]))
            {
                dist[m[i]] = dist[t] + 1;
                pre[m[i]] = {'A' + i, t};
                q.push(m[i]);
            }
        }
    }
    return -1;
}

int main()
{
    int x;
    string start = "12345678", end;
    while (cin >> x)
    {
        end += char(x + '0');
    }
    int res = bfs(start, end);
    cout << res << endl;

    string path;
    while (end != start)
    {
        path += pre[end].first;
        end = pre[end].second;
    }
    reverse(path.begin(), path.end());
    if (path.size())
        cout << path << endl;
    return 0;
}

双端队列广搜

P4554 小明的游戏 1

#include <bits/stdc++.h>

using namespace std;

typedef pair<int, int> pii;

const int N = 510;
int n,m,xx1,yy1,xx2,yy2;
char g[N][N];
int dist[N][N];
deque<pii> q;

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

int bfs(int x,int y)
{
    q.push_back({x,y});
    dist[x][y]=0;

    while (q.size())
    {
        auto t=q.front();
        q.pop_front();
        char ch=g[t.first][t.second];

        for (int i = 0; i < 4; i++)
        {
            int a=t.first+dx[i],b=t.second+dy[i];

            if(a<0||a>=n||b<0||b>=m)continue;
            if(dist[a][b]>=0)continue;

            if(g[a][b]==ch)
            {
                dist[a][b]=dist[t.first][t.second];
                q.push_front({a,b});
            }

            if(g[a][b]!=ch)
            {
                dist[a][b]=dist[t.first][t.second]+1;
                q.push_back({a,b});
            }

            if(a==xx2&&b==yy2)return dist[xx2][yy2];
        }
    }
    return -1;
}

int main()
{
    while (cin >> n >> m, n != 0 && m != 0)
    {
        for (int i = 0; i < n; i++)
        {
            cin >> g[i];
        }
        memset(dist,-1,sizeof(dist));
        q.clear();
        cin>>xx1>>yy1>>xx2>>yy2;
        int res =bfs(xx1,yy1);
        cout<<res<<'\n';
    }
    return 0;
}

————————–P3596 棋盘

————————–P4667 Switch the Lamp On

双向广搜

P1746 离开中山路 1

#include <bits/stdc++.h>

using namespace std;

typedef pair<int,int> pii;
const int N=1100;

int n;
char g[N][N];
int dist[N][N];
int vis[N][N];
pii q[N*N];
int xx1,yy1,xx2,yy2;

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

int bfs()
{
    memset(dist,-1,sizeof(dist));
    memset(vis,-1,sizeof(vis));

    dist[xx1][yy1]=0,dist[xx2][yy2]=0;
    vis[xx1][yy1]=1,vis[xx2][yy2]=2;
    q[0]={xx1,yy1},q[1]={xx2,yy2};
    int hh=0,tt=1;

    while(hh<=tt)
    {
        auto t=q[hh++];

        for (int i = 0; i < 4; i++)
        {
            int a=t.first+dx[i],b=t.second+dy[i];

            if(a<1||a>n||b<1||b>n)continue;
            if(g[a][b]!='0')continue;

            if(vis[a][b]+vis[t.first][t.second]==3)
            {
                return dist[t.first][t.second]+dist[a][b]+1;
            }

            if(dist[a][b]>=0)continue;

            dist[a][b]=dist[t.first][t.second]+1;
            if(vis[a][b]==-1)vis[a][b]=vis[t.first][t.second];
            q[++tt]={a,b};
        }
    }
    return -1;
}

int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>g[i]+1;
    }
    cin>>xx1>>yy1>>xx2>>yy2;
    int res=bfs();
    cout<<res<<'\n';
    return 0;
}

P1379 八数码难题  1

#include <bits/stdc++.h>

using namespace std;

string end1="123804765";
unordered_map<string,int> umap;
queue<string> q;

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

int bfs(string st)
{
    q.push(st);
    umap[st]=0;

    while(q.size())
    {
        auto t=q.front();
        q.pop();

        if(t==end1)return umap[t];
        int distance=umap[t];

        int a=t.find('0');
        int xx1=a/3,yy1=a%3; //一维数组下标转换为二维数组下标

        for (int i = 0; i < 4; i++)
        {
            int xx2=xx1+dx[i],yy2=yy1+dy[i];
            if(xx2<0||xx2>=3||yy2<0||yy2>=3)continue;
            int tmp=xx2*3+yy2;
            swap(t[a],t[tmp]);

            if(!umap.count(t))
            {
                umap[t]=distance+1;
                q.push(t);
            }
            swap(t[a],t[tmp]);
        }
    }
    return -1;
}

int main()
{
    string start1;
    cin>>start1;
    int res=bfs(start1);
    cout<<res;
    return 0;
}

#include <bits/stdc++.h>

using namespace std;

string start1;
string end1="123804765";
queue<string> q;
unordered_map<string,int> dist;
unordered_map<string,int> vis;

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

int bfs(string st)
{
    dist[st]=0;
    dist[end1]=0;
    q.push(st),q.push(end1);
    vis[st]=1,vis[end1]=2;


    while(q.size())
    {
        auto t=q.front();
        q.pop();

        int distance=dist[t];
        int flag=vis[t];

        int a=t.find('0');
        int xx1=a/3,yy1=a%3; //一维数组下标转换为二维数组下标

        for (int i = 0; i < 4; i++)
        {
            int xx2=xx1+dx[i],yy2=yy1+dy[i];
            if(xx2<0||xx2>=3||yy2<0||yy2>=3)continue;
            int tmp=xx2*3+yy2;
            swap(t[a],t[tmp]);

            if(vis[t]+flag==3)
            {
                int res1=dist[t];
                swap(t[a],t[tmp]);
                int res2=dist[t];
                return res1+res2+1;
            }
            if(!dist.count(t))
            {
                dist[t]=distance+1;
                vis[t]=flag;
                q.push(t);
            }
            swap(t[a],t[tmp]);
        }
    }
    return -1;
}

int main()
{
    cin>>start1;
    if(start1==end1)
    {
        cout<<0<<'\n';
        return 0;
    }
    int res=bfs(start1);
    cout<<res;
    return 0;
}

————acw1101.献给阿尔吉侬的花束

————UVA439 骑士的移动

————P1135 奇怪的电梯

————P1032 字串变换

————P1825 Corn Maze S

————more and more…知道起点和终点状态的都可以用双向BFS来优化时间


作业 (上节课DFS过不去, 这节课可以用BFS试一试~)

P1135 奇怪的电梯  dfs暴力过不去

Flood fill找连通块

P1596 Lake Counting S

P1451求细胞数量

P1331 海战

P1767 家族

SP15436 UCV2013H – Slick

迷宫问题

P1683 入门

P1605 迷宫

P1443 马的遍历

P1747 好奇怪的游戏

P2298 Mzc和男家丁的游戏

关于memset和0x3f

int a[100];

memset(a,0x3f,sizeof(a) );

0x3f=0011 1111=63

C++中int型变量所占的位数为4个字节,即32位

0x3f显然不是int型变量中单个字节的最大值,应该是0x7f=0111 1111 B

那为什么要赋值0x3f ??

int

1.作为无穷大使用

因为4个字节均为0x3f时,0x3f3f3f3f的十进制是1061109567,也就是10^ 9级别的(和0x7fffffff一个数量级),而一般场合下的数据都是小于10^9的,所以它可以作为无穷大使用而不致出现数据大于无穷大的情形。

2.可以保证无穷大加无穷大仍然不会超限。

另一方面,由于一般的数据都不会大于10^9,所以当我们把无穷大加上一个数据时,它并不会溢出(这就满足了“无穷大加一个有穷的数依然是无穷大”),事实上0x3f3f3f3f+0x3f3f3f3f=2122219134,这非常大但却没有超过32-bit int_MAX的表示范围,所以0x3f3f3f3f还满足了我们“无穷大加无穷大还是无穷大”的需求

首先要知道memset函数是以字节为单位进行赋值的;

void memset(void *s, int ch, size_t n)\;

函数解释:将s中前n个字节 (typedef unsigned int size_t )用 ch 替换并返回 s 。

其实这里面的ch就是ascii为ch的字符;

将s所指向的某一块内存中的前n个 字节的内容全部设置为ch指定的ASCII值

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇