BFS

本文详细介绍了广度优先搜索(BFS)及其在各种问题中的应用,包括拓扑排序、可达性统计、矩阵距离、最短路模型等。通过实例展示了BFS如何解决迷宫问题、电路维修、字串变换等问题,同时探讨了BFS的变形,如双端队列BFS和优先队列BFS。文章旨在帮助读者深入理解BFS及其在信息技术领域的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

广度优先搜索(BFS)

BFS介绍

树和图的广度优先遍历都需要使用一个队列来实现。
在过程中,我们不断从队头取出一个节点x,对于x面对的多条分支,把沿着每条分支到达的下一个尚未访问过的节点插入队尾。

BFS有两个重要性质:

  1. 在访问完所有的第 i i i 层节点后,才会开始访问第 i + 1 i+1 i+1 层节点。
  2. 任意时刻,队列中至多有两个层次的节点。若其中一部分节点属于第 i i i 层,那么另一部分节点就属于第 i + 1 i+1 i+1 层,并且所有第 i i i 层节点排在第 i + 1 i+1 i+1 层节点之前。也就是说,广度优先遍历队列中的元素关于层次满足“两段性”和“单调性”。

拓扑排序

参考代码如下:

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

void topsort()
{
	queue<int> q;
	for(int i = 1; i <= n; i++)
        if(!d[i]) q.push(i);
    while(q.size()) {
        int u = q.front(); q.pop();
        top_q[++cnt] = u;
        for(int i = h[u]; ~i; i = ne[i]) {
            int v = e[i];
            if(--d[v] == 0) q.push(v);
        }
    }
}

例题

可达性统计 (CH2001)

代码实现

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <bitset>

using namespace std;

const int N = 3e4 + 10;

int n, m;
int h[N], e[N], ne[N], d[N], idx;
int q[N];
int a[N], cnt;
bitset<N> f[N];

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

void topsort()
{
    int hh = 0, tt = -1;
    for(int i = 1; i <= n; i++)
        if(d[i] == 0) q[++tt] = i;
        
    while(hh <= tt)
    {
        int u = q[hh ++];
        a[cnt++] = u;
        for(int i = h[u]; ~i; i = ne[i])
            if(--d[e[i]] == 0) q[++ tt] = e[i];
    }
}

int main()
{
    memset(h, -1, sizeof h);
    cin >> n >> m;
    while(m--)
    {
        int a, b;
        cin >> a >> b;
        add(a, b);
    }
    
    topsort();
    
    for(int i = cnt - 1; i >= 0; i--)
    {
        int u = a[i];
        f[u][u] = 1;
        for(int j = h[u]; ~j; j = ne[j])
            f[u] |= f[e[j]];
    }
    
    for(int i = 1; i <= n; i++)
        cout << f[i].count() << endl;
    
    return 0;
}
Bloxorz (POJ3322)

详情见蓝书。

代码实现

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <queue>

using namespace std;

const int N = 510;

struct rec { int x, y, lie; };

int n, m;
char s[N][N];
int d[N][N][3];
rec st, ed;

const int next_x[3][4] = { {0, 0, -2, 1}, {0, 0, -1, 1}, {0, 0, -1, 2} };
const int next_y[3][4] = { {-2, 1, 0, 0}, {-1, 2, 0, 0}, {-1, 1, 0, 0} };
const int next_lie[3][4] = { {1, 1, 2, 2}, {0, 0, 1, 1}, {2, 2, 0, 0} };

inline bool valid(int x, int y)
{
    return x >= 0 && x < n && y >= 0 && y < m;
}

inline bool valid(rec t)
{
    int x = t.x, y = t.y, lie = t.lie;
    if(!valid(x, y)) return false;
    if(s[x][y] == '#') return false;
    if(lie == 0 && s[x][y] != '.') return false;
    if(lie == 1 && s[x][y + 1] == '#') return false;
    if(lie == 2 && s[x + 1][y] == '#') return false;
    return true;
}

void parse_st_ed()
{
    int dx[4] = {0, 0, -1, 1}, dy[4] = {-1, 1, 0, 0};
    for(int i = 0; i < n; i++)
        for(int j = 0; j < m; j++)
            if(s[i][j] == 'O') {
                ed.x = i, ed.y = j, ed.lie = 0, s[i][j] = '.';
            }
            else if(s[i][j] == 'X') {
                for(int k = 0; k < 4; k++) {
                    int x = i + dx[k], y = j + dy[k];
                    if(valid(x, y) && s[x][y] == 'X') {
                        st.x = min(i, x), st.y = min(j, y);
                        st.lie = k < 2 ? 1 : 2;
                        s[i][j] = s[x][y] = '.';
                        break;
                    }
                }
                if(s[i][j] == 'X') st.x = i, st.y = j, st.lie = 0;
            }
}

int bfs()
{
    for(int i = 0; i < n; i++)
        for(int j = 0; j < m; j++)
            for(int k = 0; k < 3; k++)
                d[i][j][k] = -1;
                
    queue<rec> q;
    q.push(st);
    d[st.x][st.y][st.lie] = 0;
    
    while(q.size()) {
        rec now = q.front(); q.pop();
        
        if(now.x == ed.x && now.y == ed.y && now.lie == ed.lie) return d[now.x][now.y][now.lie];
        
        for(int i = 0; i < 4; i++) {
            rec next = {now.x + next_x[now.lie][i], now.y + next_y[now.lie][i], next_lie[now.lie][i]};
            if(!valid(next)) continue;
            int x = next.x, y = next.y, lie = next.lie;
            if(d[x][y][lie] == -1) {
                d[x][y][lie] = d[now.x][now.y][now.lie] + 1;
                q.push(next);
            }
        }
    }
    
    return -1;
}

int main()
{
    while(scanf("%d %d", &n, &m) != EOF && n)
    {
        for(int i = 0; i < n; i++) scanf("%s", s[i]);
        parse_st_ed();
        int ans = bfs();
        if(ans == -1) puts("Impossible");
        else printf("%d\n", ans);
    }
    return 0;
}
矩阵距离 (CH2501)

题目描述:

给定一个 N N N M M M 列的 01 01 01 矩阵 A A A A [ i ] [ j ] A[i][j] A[i][j] A [ k ] [ l ] A[k][l] A[k][l] 之间的曼哈顿距离定义为:

d i s t ( A [ i ] [ j ] , A [ k ] [ l ] ) = ∣ i − k ∣ + ∣ j − l ∣ d i s t ( A [ i ] [ j ] , A [ k ] [ l ] ) = ∣ i − k ∣ + ∣ j − l ∣ dist(A[i][j],A[k][l])=|i−k|+|j−l|dist(A[i][j],A[k][l])=|i−k|+|j−l| dist(A[i][j],A[k][l])=ik+jldist(A[i][j],A[k][l])=ik+jl

输出一个 N N N M M M 列的整数矩阵 B B B,其中:

B [ i ] [ j ] = m i n 1 ≤ x ≤ N , 1 ≤ y ≤ M , A [ x ] [ y ] = 1 d i s t ( A [ i ] [ j ] , A [ x ] [ y ] ) B[i][j]=min1≤x≤N,1≤y≤M,A[x][y]=1dist(A[i][j],A[x][y])

### 关于洛谷平台上的 BFS 算法题目与教程 #### 什么是广度优先搜索 (BFS)? 广度优先搜索是一种用于图结构的遍历或搜索算法,它按照层次顺序访问节点。通常使用队列数据结构来实现该算法[^2]。 #### 洛谷平台上的经典 BFS 题目 以下是几类常见的 BFS 类型题目及其特点: 1. **八数码问题** 八数码问题是经典的 BFS 应用之一。尽管可以用朴素 BFS 解决,但在某些情况下可能会因为状态空间过大而超时。因此,可以考虑采用 A* 启发式搜索算法优化求解过程[^1]。 ```python from collections import deque def bfs(start_state, goal_state): queue = deque([(start_state, 0)]) visited = set([start_state]) while queue: current_state, steps = queue.popleft() if current_state == goal_state: return steps for next_state in generate_next_states(current_state): if next_state not in visited: visited.add(next_state) queue.append((next_state, steps + 1)) return -1 ``` 2. **棋盘路径问题** 这是一类基于网格地图的经典 BFS 题目。例如,在 \( n \times m \) 的棋盘上,给定起点和目标点,计算最短步数。这类问题可以通过定义合法移动方向并逐层扩展的方式解决[^3]。 3. **多源 BFS 和双端 BFS** 多源 BFS 是指从多个起始位置同时出发进行搜索;双端 BFS 则是从起点和终点分别向中间靠拢,从而减少搜索范围。这种技术特别适用于复杂场景下的最优路径规划[^4]。 #### 学习资源推荐 对于初学者来说,《算法竞赛入门经典》以及《挑战程序设计竞赛》都是很好的参考资料。此外,还可以参考以下链接获取更多实战练习机会: - [洛谷 P1379](https://www.luogu.com.cn/problem/P1379): 结合启发式搜索深入理解 BFS。 - [洛谷 BFS专题训练](https://www.luogu.com.cn/training/...): 提供了一系列由浅入深的学习材料。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值