林老师 · 客观题题库 · CSP-J 学习卷 L27 · 图论遍历与泛洪

CSP-J 学习卷 L27 · 图论遍历与泛洪

入门级算法 · 图的 DFS/BFS 遍历 / 连通分量 / 泛洪与岛屿问题族 / 二分图·判环·时间戳
真题
复刻
试卷编号LEARN-J-27
题目总数88 题 · 107 分
试卷类型客观题
考生须知:
① 本卷共 7 大部分,合计 88 题 · 107 分,全部为客观题;
② 试卷右上角设有 「提交答卷」 与 「重置考试」 按钮,提交后系统自动判分并显示答题正确情况,请确认全部作答后再行提交;
③ 试卷不显示答案,提交后方可查看每题作答与正确答案的对照;
④ 答卷进度会保留在本地缓存中,刷新或再次打开仍可继续作答;
⑤ 本卷仅供学生练习使用;请勿用于其他用途;题面有问题请联系:i64coder@163.com。

判 分 报 告

0 / 107 分
0
答 对 · 得 0 分
0
答 错 · 失 0 分
当前筛选下没有题目
壹

图上的深度优先遍历

17 计分点 · 每点 1 分
第 1 题 单选 未作答

遍历一棵树不需要「访问标记」——从根往下走每个节点只会被走到一次。但遍历一张图(比如下图这样的带环无向图)就必须给每个点打访问标记。为什么?

01void dfs(int u) {
02    cout << u << " ";
03    vis[u] = true;
04    for (int v : adj[u])
05        if (!vis[v]) dfs(v);
06}

(1 分)
原创教学题 | 考点:图的 DFS 为什么必须 vis
第 2 题 单选 未作答

下面的图 DFS 里,vis[u] = true; 被误放在了递归调用之后(进函数先递归、返回后才标记)。对一条只有 2 个顶点 0-1 的边运行 dfs(0),会发生什么?

01void dfs(int u) {
02    for (int v : adj[u])
03        if (!vis[v]) dfs(v);
04    vis[u] = true;
05}

(1 分)
原创教学题 | 考点:vis 标记放在递归之后为什么错
第 3 题 单选 未作答

上一课学搜索回溯时,全排列代码里 used[i] = false; 的「恢复现场」一行万万不能删。而图的 DFS 遍历里,vis[u] = true; 却从不恢复。同样是标记,为什么一个要恢复、一个不要?

01void dfs(int u) {
02    vis[u] = true;
03    for (int v : adj[u]) if (!vis[v]) dfs(v);
04}
05void perm(int k) {
06    for (int i = 1; i <= n; i++) {
07        if (used[i]) continue;
08        used[i] = true;
09        perm(k + 1);
10        used[i] = false;
11    }
12}

(1 分)
原创教学题 | 考点:图遍历不恢复现场 vs 搜索回溯恢复
第 4 题 单选 未作答

下面的程序对图做深度优先遍历,dfs 每进入一个点就输出它。输出的访问顺序是什么?

01#include <iostream>
02#include <vector>
03using namespace std;
04vector<int> adj[6];
05bool vis[6];
06void dfs(int u) {
07    cout << u << " ";
08    vis[u] = true;
09    for (int v : adj[u])
10        if (!vis[v]) dfs(v);
11}
12int main() {
13    adj[0] = {1, 2};
14    adj[1] = {3, 4};
15    adj[2] = {5};
16    adj[3] = {};
17    adj[4] = {};
18    adj[5] = {};
19    dfs(0);
20    return 0;
21}

(1 分)
原创教学题 | 考点:递归 DFS 访问序追踪
第 5 题 单选 未作答

改用「显式栈」实现 DFS:先把起点压栈并标记,每次弹出栈顶访问它,再把它的未访问邻居按邻接表顺序依次压栈(压入时立即标记)。对上一题相同的图,从 0 开始,访问顺序是什么?

01void dfs(int start) {
02    vector<int> st;
03    st.push_back(start);
04    vis[start] = true;
05    while (!st.empty()) {
06        int u = st.back();
07        st.pop_back();
08        cout << u << " ";
09        for (int v : adj[u]) {
10            if (!vis[v]) {
11                vis[v] = true;
12                st.push_back(v);
13            }
14        }
15    }
16}

(1 分)
原创教学题 | 考点:显式栈 DFS 访问序追踪
第 6 题 单选 未作答

对同一张图,递归 DFS 的访问序是 0 1 3 4 2 5,而显式栈写法(邻接表正序压栈)的访问序是 0 2 5 1 4 3。为什么两者不一样?下面两段是同一个图(0 连 1、2;1 连 3、4;2 连 5)的两种实现:

01#include <iostream>
02#include <vector>
03using namespace std;
04vector<int> adj[6];
05bool vis[6];
06void dfs(int u) {
07    cout << u << " ";
08    vis[u] = true;
09    for (int v : adj[u])
10        if (!vis[v]) dfs(v);
11}
12void dfs_stack(int start) {
13    vector<int> st;
14    st.push_back(start);
15    vis[start] = true;
16    while (!st.empty()) {
17        int u = st.back();
18        st.pop_back();
19        cout << u << " ";
20        for (int v : adj[u]) {
21            if (!vis[v]) {
22                vis[v] = true;
23                st.push_back(v);
24            }
25        }
26    }
27}
28int main() {
29    adj[0] = {1, 2};
30    adj[1] = {0, 3, 4};
31    adj[2] = {0, 5};
32    adj[3] = {1};
33    adj[4] = {1};
34    adj[5] = {2};
35    dfs(0);
36    cout << endl;
37    for (int i = 0; i < 6; i++) vis[i] = false;
38    dfs_stack(0);
39    cout << endl;
40    return 0;
41}

(1 分)
原创教学题 | 考点:显式栈与递归序不同的原因
第 7 题 单选 未作答

对 VV 个顶点、EE 条边的无向图,用邻接表遍历和用邻接矩阵遍历(每次都要扫描整行找邻居),时间复杂度各是多少?

(1 分)
原创教学题 | 考点:邻接表与邻接矩阵遍历复杂度
第 8 题 单选 未作答

一张稀疏图(比如 V=104V=10^4 个点、E=104E=10^4 条边),用邻接表遍历只要 O(V+E)O(V+E),用邻接矩阵遍历却要 O(V2)O(V^2)。为什么矩阵写法每走一个点都要白看那么多不存在的边?

(1 分)
原创教学题 | 考点:邻接矩阵为什么是 O(V^2)
第 9 题 单选 未作答

图的输入里可能有自环(一个点连自己)和重边(两个点之间有多条边)。DFS 遍历时,这两种边分别会被怎么处理?

(1 分)
原创教学题 | 考点:自环与重边的处理
第 10 题 单选 未作答

下面的图有一个三角形(0-1、1-2、2-0)再连一条 1-3。程序从 0 开始 DFS,输出什么顺序?

01#include <iostream>
02#include <vector>
03using namespace std;
04vector<int> adj[4];
05bool vis[4];
06void dfs(int u) {
07    cout << u << " ";
08    vis[u] = true;
09    for (int v : adj[u])
10        if (!vis[v]) dfs(v);
11}
12int main() {
13    adj[0] = {1, 2};
14    adj[1] = {0, 2, 3};
15    adj[2] = {0, 1};
16    adj[3] = {1};
17    dfs(0);
18    return 0;
19}

(1 分)
原创教学题 | 考点:三角形图上 DFS 访问序
第 11 题 单选 未作答

对上面那题「三角形 + 一条 1-3」的图(4 个点、5 条边),从 0 开始 DFS。走过的「树边」(第一次发现新点用的边)有几条?被 vis 挡掉的「回边」有几条?

(1 分)
原创教学题 | 考点:树边与回边计数
第 12 题 单选 未作答

补全邻接表深度优先遍历模板的两处空缺,使每个点恰好访问一次。

01void dfs(int u) {
02    cout << u << " ";
03    ____;
04    for (int v : adj[u])
05        if (!vis[v]) ____;
06}

(1 分)
原创教学题 | 考点:补全邻接表 DFS 模板
第 13 题 单选 未作答

下面的 DFS 有一处明显的错误:把标记写成了 vis[u] = false。对只有一条边 0-1 的图运行 dfs(0),会发生什么?

01void dfs(int u) {
02    cout << u << " ";
03    vis[u] = false;
04    for (int v : adj[u])
05        if (!vis[v]) dfs(v);
06}

(1 分)
原创教学题 | 考点:找错:把 vis 标成了 false
第 14~17 题 阅读程序 (共 4 分) 未作答

题目:给一张 nn 个顶点、mm 条边的无向图(编号 0∼n−10 \sim n-1),从顶点 0 开始做深度优先遍历,输出访问到的每个顶点(按访问顺序)。本题数据:n = 6,边为 (0,1)、(0,2)、(1,3)、(1,4)、(2,5)。

01#include <iostream>
02#include <vector>
03using namespace std;
04const int N = 10;
05vector<int> adj[N];
06bool vis[N];
07void addEdge(int u, int v) {
08    adj[u].push_back(v);
09    adj[v].push_back(u);
10}
11void dfs(int u) {
12    cout << u << " ";
13    vis[u] = true;
14    for (int v : adj[u])
15        if (!vis[v]) dfs(v);
16}
17int main() {
18    addEdge(0, 1); addEdge(0, 2);
19    addEdge(1, 3); addEdge(1, 4);
20    addEdge(2, 5);
21    dfs(0);
22    return 0;
23}

14.

本题第一问:程序输出的顶点序列是什么?

(1 分)
15.

本题第二问:为什么 vis[u] = true; 必须放在循环之前、进函数就执行?

(1 分)
16.

本题第三问:若把 if (!vis[v]) dfs(v); 里的 vis[v] 误写成 vis[u],程序会怎样?

(1 分)
17.

本题第四问:把加边顺序改成 addEdge(0, 2); addEdge(0, 1);(其余不变),输出序列变成什么?

(1 分)
原创教学题 | 典型代码:邻接表 DFS 完整模板
贰

图上的广度优先遍历

17 计分点 · 每点 1 分
第 18 题 单选 未作答

在无权无向图上从起点做广度优先遍历,下列说法哪个正确?

(1 分)
原创教学题 | 考点:BFS 层数与最短距离
第 19 题 单选 未作答

同样是无权图求最短路径,为什么 BFS 第一次访问到目标点时路径必然最短,而 DFS 却不行?

(1 分)
原创教学题 | 考点:BFS 能求最短路而 DFS 不能
第 20 题 单选 未作答

BFS 遍历图时,每个顶点为什么只会入队一次(第一次被发现时),之后即使再有邻居指向它也不再入队?看下面这段带 dist 的 BFS:

01#include <iostream>
02#include <queue>
03#include <vector>
04using namespace std;
05vector<int> adj[105];
06int dist[105];
07void bfs(int s) {
08    for (int i = 0; i < 105; i++) dist[i] = -1;
09    dist[s] = 0;
10    queue<int> q;
11    q.push(s);
12    while (!q.empty()) {
13        int u = q.front();
14        q.pop();
15        for (int v : adj[u])
16            if (dist[v] == -1) {
17                dist[v] = dist[u] + 1;
18                q.push(v);
19            }
20    }
21}

(1 分)
原创教学题 | 考点:图上 BFS 为什么每点只入队一次
第 21 题 单选 未作答

下面的 BFS 把「标记」改成了在 q.pop() 之后才检查入没入过队(即出队时才标记)。对一条只有 0-1 两个点的无向边运行,会发生什么?

01queue<int> q;
02q.push(0);
03while (!q.empty()) {
04    int u = q.front();
05    q.pop();
06    vis[u] = true;
07    for (int v : adj[u])
08        if (!vis[v]) q.push(v);
09}

(1 分)
原创教学题 | 考点:入队时标记 vs 出队时标记
第 22 题 单选 未作答

下面的 BFS 从 0 开始,最终 dist[0] 到 dist[4] 依次输出什么?

01#include <iostream>
02#include <queue>
03#include <vector>
04using namespace std;
05vector<int> adj[5];
06int dist[5];
07int main() {
08    adj[0] = {1, 2};
09    adj[1] = {0, 3};
10    adj[2] = {0, 3, 4};
11    adj[3] = {1, 2};
12    adj[4] = {2};
13    queue<int> q;
14    for (int i = 0; i < 5; i++) dist[i] = -1;
15    dist[0] = 0;
16    q.push(0);
17    while (!q.empty()) {
18        int u = q.front(); q.pop();
19        for (int v : adj[u])
20            if (dist[v] == -1) {
21                dist[v] = dist[u] + 1;
22                q.push(v);
23            }
24    }
25    for (int i = 0; i < 5; i++) cout << dist[i] << " ";
26    return 0;
27}

(1 分)
原创教学题 | 考点:BFS 距离表追踪
第 23 题 单选 未作答

对上面那题的图(0-1、0-2、1-3、2-3、2-4),BFS 从 0 出发,到点 4 的最短路径长度是多少?

(1 分)
原创教学题 | 考点:0 到 4 的最短路径长度
第 24 题 单选 未作答

对同一张图(0 连 1、2;1、2 都连 3;2 连 4),BFS 从 0 出发,第 0、1、2 层分别有哪些点?

(1 分)
原创教学题 | 考点:BFS 分层输出
第 25 题 单选 未作答

补全 BFS 模板,使每个点按层入队、距离正确。

01queue<int> q;
02dist[start] = 0;
03q.push(start);
04while (!q.empty()) {
05    int u = q.front(); q.pop();
06    for (int v : adj[u])
07        if (____) {
08            ____;
09            q.push(v);
10        }
11}

(1 分)
原创教学题 | 考点:补全 BFS 模板
第 26 题 单选 未作答

上一课在网格上做过 BFS 走迷宫。把网格上的 BFS 看成「图上的 BFS」,下面哪个说法正确?

(1 分)
原创教学题 | 考点:网格 BFS 与图上 BFS 同构
第 27 题 单选 未作答

在有向图上跑 BFS(只沿出边扩展)和无向图有什么不同?

(1 分)
原创教学题 | 考点:有向图 BFS 只沿出边
第 28 题 单选 未作答

有向图的边为 0→1、0→2、1→3、3→4。从 0 出发跑 BFS,dist[0] 到 dist[4] 分别是多少?

(1 分)
原创教学题 | 考点:有向图 BFS 距离追踪
第 29 题 单选 未作答

图里有 5 个点,边为 0-1、0-2、1-3、2-3,点 4 是孤立点(没有边)。从 0 出发跑 BFS,输出 dist[0] 到 dist[4],结果是什么?

(1 分)
原创教学题 | 考点:非连通图 BFS 的 dist 保持 -1
第 30 题 单选 未作答

很多 BFS 模板不单独开 vis 数组,直接用 dist[v] == -1 判断是否访问过。为什么这样可以?

01queue<int> q;
02dist[start] = 0;
03q.push(start);
04while (!q.empty()) {
05    int u = q.front(); q.pop();
06    for (int v : adj[u])
07        if (dist[v] == -1) {
08            dist[v] = dist[u] + 1;
09            q.push(v);
10        }
11}

(1 分)
原创教学题 | 考点:dist 同时充当 visited
第 31~34 题 阅读程序 (共 4 分) 未作答

题目:给一张 nn 个顶点、mm 条边的无向图(编号 0∼n−10 \sim n-1),从顶点 0 出发做广度优先遍历,输出每个点到 0 的最短距离(不可达输出 -1)。本题数据:n = 5,边为 (0,1)、(0,2)、(1,3)、(2,3)、(2,4)。

01#include <iostream>
02#include <queue>
03#include <vector>
04using namespace std;
05const int N = 10;
06vector<int> adj[N];
07int dist[N];
08void addEdge(int u, int v) {
09    adj[u].push_back(v);
10    adj[v].push_back(u);
11}
12int main() {
13    addEdge(0, 1); addEdge(0, 2);
14    addEdge(1, 3); addEdge(2, 3); addEdge(2, 4);
15    queue<int> q;
16    for (int i = 0; i < 5; i++) dist[i] = -1;
17    dist[0] = 0;
18    q.push(0);
19    while (!q.empty()) {
20        int u = q.front(); q.pop();
21        for (int v : adj[u])
22            if (dist[v] == -1) {
23                dist[v] = dist[u] + 1;
24                q.push(v);
25            }
26    }
27    for (int i = 0; i < 5; i++) cout << dist[i] << " ";
28    return 0;
29}

31.

本题第一问:程序输出什么?

(1 分)
32.

本题第二问:为什么这里用 dist[v] == -1 判断而没有单独开 vis 数组?

(1 分)
33.

本题第三问:若把 dist[v] == -1 误写成 dist[v] != -1,程序输出什么?

(1 分)
34.

本题第四问:若把图改成有向图,边只保留方向 0→1、0→2、1→3、3→4(删掉反向边),程序输出变成什么?

(1 分)
原创教学题 | 典型代码:邻接表 BFS 完整模板
叁

连通性与连通分量

13 计分点 · 每点 1 分
第 35 题 单选 未作答

下面的代码统计无向图的连通分量个数。主循环的 for (int i = 0; i < n; i++) 每次遇到一个未访问顶点就调用一次 dfs,并让 cnt++。请问 cnt 的含义是?

01int cnt = 0;
02for (int i = 0; i < n; i++)
03    if (!vis[i]) {
04        cnt++;
05        dfs(i);
06    }

(1 分)
原创教学题 | 考点:连通块计数代码
第 36 题 单选 未作答

无向图有 6 个点,边为 0-1、2-3、4-5(三条互不相连的边)。跑连通分量计数代码(主循环 for 0 到 5),cnt 最终是多少?

01int cnt = 0;
02for (int i = 0; i < n; i++)
03    if (!vis[i]) {
04        cnt++;
05        dfs(i);
06    }

(1 分)
原创教学题 | 考点:三连通分量计数追踪
第 37 题 单选 未作答

如果只调用一次 dfs(0) 就结束,不写主循环的 for,对一张非连通图(比如两个分离的三角形)会发生什么?

01int cnt = 0;
02for (int i = 0; i < n; i++)
03    if (!vis[i]) {
04        cnt++;
05        dfs(i);
06    }

(1 分)
原创教学题 | 考点:主循环为什么要 for 所有顶点
第 38 题 单选 未作答

连通分量计数时,前一个分量的 vis 标记跨到下一个分量继续保留。为什么不把 vis 清空重来?

(1 分)
原创教学题 | 考点:vis 跨分量保留为什么重要
第 39 题 单选 未作答

从点 u 出发做一次 DFS(或 BFS),为什么只能覆盖 u 所在的那个连通分量,绝不可能跑到另一个分量里去?下面用一段分量计数代码示意:

01#include <iostream>
02#include <vector>
03using namespace std;
04vector<int> adj[105];
05bool vis[105];
06void dfs(int u) {
07    vis[u] = true;
08    for (int v : adj[u])
09        if (!vis[v]) dfs(v);
10}
11void bfs(int s) {
12    queue<int> q;
13    q.push(s);
14    vis[s] = true;
15    while (!q.empty()) {
16        int u = q.front();
17        q.pop();
18        for (int v : adj[u])
19            if (!vis[v]) {
20                vis[v] = true;
21                q.push(v);
22            }
23    }
24}
25int main() {
26    adj[0] = {1};
27    adj[1] = {0};
28    adj[2] = {3};
29    adj[3] = {2};
30    dfs(0);
31    cout << (vis[1] ? 1 : 0) << " " << (vis[2] ? 1 : 0) << endl;
32    return 0;
33}

(1 分)
原创教学题 | 考点:为什么一次遍历只覆盖一个分量
第 40 题 单选 未作答

判断无向图上两点 u、v 是否连通,常见做法是「从 u 跑一次 DFS/BFS,然后看 vis[v]」。为什么看 vis[v] 就够了?

(1 分)
原创教学题 | 考点:判断两点是否连通
第 41 题 单选 未作答

无向图有 4 个点,边为 0-1、0-2、1-2(一个三角形)和 2-3。连通分量计数输出多少?

(1 分)
原创教学题 | 考点:连通分量数:4 点图
第 42~45 题 阅读程序 (共 4 分) 未作答

题目:给一张 nn 个顶点、mm 条边的无向图(编号 0∼n−10 \sim n-1),统计连通分量的个数。本题数据:n = 7,边为 (0,1)、(0,2)、(1,2)、(3,4),点 5、6 是孤立点。

01#include <iostream>
02#include <vector>
03using namespace std;
04const int N = 10;
05vector<int> adj[N];
06bool vis[N];
07void addEdge(int u, int v) {
08    adj[u].push_back(v);
09    adj[v].push_back(u);
10}
11void dfs(int u) {
12    vis[u] = true;
13    for (int v : adj[u])
14        if (!vis[v]) dfs(v);
15}
16int main() {
17    addEdge(0, 1); addEdge(0, 2); addEdge(1, 2);
18    addEdge(3, 4);
19    int n = 7, cnt = 0;
20    for (int i = 0; i < n; i++)
21        if (!vis[i]) {
22            cnt++;
23            dfs(i);
24        }
25    cout << cnt << endl;
26    return 0;
27}

42.

本题第一问:程序输出多少?

(1 分)
43.

本题第二问:孤立点(如 5)为什么也算一个连通分量?

(1 分)
44.

本题第三问:若把主循环写成 for (int i = 0; i < n; i++) dfs(i);(去掉 if (!vis[i]) 和 cnt++),程序会怎样?

(1 分)
45.

本题第四问:若把 dfs 里的 vis[u] = true; 删掉,程序输出多少?

(1 分)
原创教学题 | 典型代码:无向图连通分量计数
第 46 题 单选 未作答

有向图上从 u 能走到 v,能否推出从 v 能走到 u?下面哪个说法正确?

(1 分)
原创教学题 | 考点:有向图可达性不对称
第 47 题 单选 未作答

有向图边为 0→1、0→2、1→3、3→4。从 0 出发(沿出边)能到达哪些点?从 2 出发呢?

(1 分)
原创教学题 | 考点:有向图可达集追踪
肆

泛洪与岛屿问题族

23 计分点 · 每点 1 分
第 48 题 单选 未作答

上一课学过网格上的 Flood Fill(泛洪)。把泛洪放进「图遍历」的框架里看,正确的说法是?

(1 分)
原创教学题 | 考点:泛洪本质是图遍历
第 49 题 单选 未作答

5×55 \times 5 网格用 1 表示陆地、0 表示水,陆地分布为:(0,0)、(0,1)、(1,1) 构成一个 L 形,另有 (2,3)、(4,4) 两个独立陆地格。用四连通泛洪数陆地连通块,有几块?

(1 分)
原创教学题 | 考点:岛屿数量:L形加两个单点
第 50 题 单选 未作答

4×44 \times 4 网格陆地分布:左上 (0,0)(0,1)(1,0) 组成一块,右下 (1,3)(2,2)(2,3)(3,1)(3,2)(3,3) 组成一块。四连通下最大陆地块的面积(格子数)是多少?

(1 分)
原创教学题 | 考点:最大连通块面积
第 51 题 单选 未作答

用「每格 4 条边,相邻两格共享边各减 1」的规则算周长。单个陆地格周长是 4;三个陆地格排成一条直线(1×3),周长是多少?

(1 分)
原创教学题 | 考点:周长:单格与直线
第 52 题 单选 未作答

计算岛屿周长时,「每格先算 4 条边,再减去相邻对数 × 2」。为什么两个相邻格子要减 2 而不是减 1?

(1 分)
原创教学题 | 考点:周长为什么是 4 减相邻对×2
第 53 题 单选 未作答

3×33 \times 3 网格全部是陆地,用公式 4×格数−2×相邻对数4 \times 格数 - 2 \times 相邻对数 计算周长是多少?

(1 分)
原创教学题 | 考点:3×3 全 1 周长
第 54 题 单选 未作答

「把被 1 包围的 0 全部改成 1」这类题,标准做法是从边界的 0 出发反向泛洪,把所有能到边界的 0 标记出来,剩下的 0 才是被包围的。为什么不直接对每个 0 判断它四周是不是全 1?

(1 分)
原创教学题 | 考点:被包围的岛屿:边界反向泛洪
第 55 题 单选 未作答

4×44 \times 4 网格陆地分布为:第 1、4 行和第 1、4 列全是 1,内部 (1,1)、(1,2)、(2,2) 是 0,(2,1) 是 1。(下标从 0 开始)用「从边界 0 反向泛洪」的方法,会被翻转成 1 的 0 有几个?

(1 分)
原创教学题 | 考点:被包围的 0 的个数
第 56 题 单选 未作答

3×33 \times 3 网格,只有中心 (1,1) 是 1(源),其余是 0。用「多源 BFS」计算每个格子到最近的 1 的曼哈顿步数(上下左右四连通),输出距离表是什么?

(1 分)
原创教学题 | 考点:多源 BFS:到最近源的距离
第 57 题 单选 未作答

3×33 \times 3 网格,源点分别在 (0,0) 和 (2,2)。多源 BFS 到最近源的距离表是什么?(四连通)

(1 分)
原创教学题 | 考点:双源 BFS 距离表
第 58 题 单选 未作答

「求每个格子到最近源点的距离」,可以把所有源点一次性全部入队再 BFS,第一圈就同时从多个源扩散。为什么这样得到的是「到最近源」的距离而不是「到某个源」的?

(1 分)
原创教学题 | 考点:为什么多源 BFS 正确
第 59 题 单选 未作答

一个 3×33 \times 3 图像,中心 (1,1) 是 r,其余 8 格都是 g。用图像填充(油漆桶)把起点颜色改成 y,最终中心变成 y,周围 8 格还是 g。为什么只有 1 格变色?

(1 分)
原创教学题 | 考点:图像填充:单色块扩散
第 60 题 单选 未作答

2×22 \times 2 图像全为 g,从 (0,0) 发起填充改成 y。最终图像是什么?

(1 分)
原创教学题 | 考点:图像填充:全连通块变色
第 61 题 单选 未作答

图像填充代码里,第一步总是先记下起点颜色再入队扩散。为什么必须记这个原色,而不是在扩散时看「邻居和当前格同色」?

01int prev = image[cur.r][cur.c];
02queue<Point> q;
03q.push(cur);
04while (!q.empty()) {
05    Point p = q.front(); q.pop();
06    for (Point np : neighbors(p))
07        if (image[np.r][np.c] == prev) {
08            image[np.r][np.c] = new_color;
09            q.push(np);
10        }
11}

(1 分)
原创教学题 | 考点:图像填充为什么先记起点原色
第 62 题 单选 未作答

泛洪(Flood Fill)用 BFS 实现时,也要遵守「入队即标记」。如果改成出队时才标记,会怎样?下面这段是「入队即标记」的正确版本:

01#include <iostream>
02#include <queue>
03using namespace std;
04char a[105][105];
05bool vis[105][105];
06int n, m;
07const int dx[4] = {1, -1, 0, 0};
08const int dy[4] = {0, 0, 1, -1};
09void flood(int sx, int sy) {
10    queue<pair<int, int> > q;
11    q.push({sx, sy});
12    vis[sx][sy] = true;
13    while (!q.empty()) {
14        int x = q.front().first, y = q.front().second;
15        q.pop();
16        for (int d = 0; d < 4; d++) {
17            int nx = x + dx[d], ny = y + dy[d];
18            if (nx < 0 || nx >= n || ny < 0 || ny >= m) continue;
19            if (a[nx][ny] != a[sx][sy]) continue;
20            if (vis[nx][ny]) continue;
21            vis[nx][ny] = true;
22            q.push({nx, ny});
23        }
24    }
25}

(1 分)
原创教学题 | 考点:泛洪入队即标记
第 63 题 单选 未作答

一个 2000×20002000 \times 2000 网格全部是陆地,用递归 DFS 实现泛洪会栈溢出崩溃,而用 BFS 队列就能正常跑完。为什么?

(1 分)
原创教学题 | 考点:大网格递归泛洪为什么爆栈
第 64 题 单选 未作答

大范围网格泛洪时,为了不爆栈,应该优先选哪种实现?

(1 分)
原创教学题 | 考点:泛洪模板选型
第 65 题 单选 未作答

同一张网格图,四连通和八连通会得到不同的岛屿数量。从「图」的角度看,原因是什么?

(1 分)
原创教学题 | 考点:四连通与八连通对应不同边集
第 66 题 单选 未作答

2×22 \times 2 网格,(0,0) 和 (1,1) 是陆地,另外两格是水。四连通下有几块陆地?八连通下有几块?

(1 分)
原创教学题 | 考点:四连通八连通块数对比
第 67~70 题 阅读程序 (共 4 分) 未作答

题目:给一个 n×mn \times m 的 0/1 网格(1 是陆地、0 是水),四连通下数一数有多少个陆地连通块(岛屿)。本题数据:n = 3, m = 3,网格为 [1 0 1; 1 0 0; 1 1 1]。

01#include <iostream>
02#include <queue>
03using namespace std;
04const int N = 10;
05int a[N][N];
06bool vis[N][N];
07int n, m;
08int dx[4] = {1, -1, 0, 0};
09int dy[4] = {0, 0, 1, -1};
10void bfs(int x, int y) {
11    queue<pair<int, int> > q;
12    q.push(make_pair(x, y));
13    vis[x][y] = true;
14    while (!q.empty()) {
15        pair<int, int> u = q.front(); q.pop();
16        for (int d = 0; d < 4; d++) {
17            int nx = u.first + dx[d];
18            int ny = u.second + dy[d];
19            if (nx < 0 || nx >= n || ny < 0 || ny >= m) continue;
20            if (vis[nx][ny] || a[nx][ny] == 0) continue;
21            vis[nx][ny] = true;
22            q.push(make_pair(nx, ny));
23        }
24    }
25}
26int main() {
27    n = 3; m = 3;
28    int init[3][3] = {1, 0, 1, 1, 0, 0, 1, 1, 1};
29    for (int i = 0; i < n; i++)
30        for (int j = 0; j < m; j++) a[i][j] = init[i][j];
31    int ans = 0;
32    for (int i = 0; i < n; i++)
33        for (int j = 0; j < m; j++)
34            if (a[i][j] == 1 && !vis[i][j]) {
35                ans++;
36                bfs(i, j);
37            }
38    cout << ans << endl;
39    return 0;
40}

67.

本题第一问:程序输出多少?

(1 分)
68.

本题第二问:为什么要在 vis[x][y] = true 之后才入队?如果把这行删掉会怎样?

(1 分)
69.

本题第三问:如果把方向数组的 dy 顺序改错,比如 dy = {0, 0, -1, 1}(把上和下写反),程序结果会变吗?

(1 分)
70.

本题第四问:若把主循环的 if (a[i][j] == 1 && !vis[i][j]) 误写成 if (!vis[i][j])(去掉陆地判断),程序输出多少?

(1 分)
原创教学题 | 典型代码:岛屿数量(0/1 四连通)
伍

遍历的应用:二分图、判环与时间戳

15 计分点 · 每点 1 分
第 71 题 单选 未作答

染色法判断二分图:给每个点染色,保证「相邻的两个点颜色不同」。如果能做到,这个图就是二分图。下面哪个说法正确?

(1 分)
原创教学题 | 考点:二分图判定:相邻异色
第 72 题 单选 未作答

对「4 个点的环」(边 0-1、1-2、2-3、3-0)染色:0 染 0 色,逐点扩散,结果是什么?

(1 分)
原创教学题 | 考点:4 环与三角的染色
第 73 题 单选 未作答

三角形(3 个点两两相连)染色时必然冲突,为什么?更一般地,为什么「奇数长度的环」必不是二分图?

(1 分)
原创教学题 | 考点:为什么奇环不是二分图
第 74 题 单选 未作答

下面哪个图是二分图?(都是简单无向图)

(1 分)
原创教学题 | 考点:三角形与星形染色判定
第 75 题 单选 未作答

对三角形染色:0 染 0 色,0-1、0-2 两条边让 1、2 都染 1 色。接下来检查边 1-2 时发现什么?

(1 分)
原创教学题 | 考点:染色法冲突点追踪
第 76 题 单选 未作答

无向图用 DFS 判环:dfs(u) 扫描邻居 v 时,如果 v 已访问且 v != parent[u],就说明有环。为什么「已访问的父节点」不算环?

01bool dfs(int u, int parent) {
02    vis[u] = true;
03    for (int v : adj[u]) {
04        if (!vis[v]) {
05            if (dfs(v, u)) return true;
06        } else if (v != parent) {
07            return true;
08        }
09    }
10    return false;
11}

(1 分)
原创教学题 | 考点:无向图判环:非父邻点
第 77 题 单选 未作答

对「三角形 0-1、1-2、2-0」用无向判环 DFS 跑一遍,下面的说法哪个正确?

(1 分)
原创教学题 | 考点:无向图判环追踪
第 78 题 单选 未作答

有向图判环用三色标记:白=未访问、灰=正在递归栈中、黑=已处理完。为什么「仅用 visited(布尔)」在有向图上不够?下面是一段三色判环代码:

01#include <iostream>
02#include <vector>
03using namespace std;
04vector<int> adj[105];
05int color[105];
06bool dfs(int u) {
07    color[u] = 1;
08    for (int v : adj[u]) {
09        if (color[v] == 1) return true;
10        if (color[v] == 0) {
11            if (dfs(v)) return true;
12        }
13    }
14    color[u] = 2;
15    return false;
16}

(1 分)
原创教学题 | 考点:有向图判环为什么需要三色
第 79 题 单选 未作答

对三个有向图分别用三色 DFS 判环:A:0→1、1→2(一条链);B:0→1、1→2、2→0(三条边成环);C:0→1、1→2、0→2(两条路都到 2)。下面哪个说法正确?

(1 分)
原创教学题 | 考点:有向图判环:黑点不算环
第 80 题 单选 未作答

有向图 0→1、0→2、1→3、2→3(一个「菱形」DAG)。三色 DFS 判环结果是什么?

(1 分)
原创教学题 | 考点:DAG 判环
第 81 题 单选 未作答

DFS 时间戳给每个点记录进入时刻 in 和离开时刻 out(括号序)。判断「u 是不是 v 的祖先」,为什么看区间包含就够?看下面的括号序骨架:

01#include <iostream>
02#include <vector>
03using namespace std;
04vector<int> adj[105];
05int in[105], out[105];
06int timer;
07void dfs(int u) {
08    in[u] = ++timer;
09    for (int v : adj[u]) dfs(v);
10    out[u] = ++timer;
11}

(1 分)
原创教学题 | 考点:时间戳与祖先判定
第 82 题 单选 未作答

树 0 连 1、2;1 连 3;2 连 4、5。DFS 从 0 开始记录进入/离开时间戳(每进入+1、每离开+1)。下面哪组 in/out 是正确的?

(1 分)
原创教学题 | 考点:时间戳追踪
第 83 题 单选 未作答

用上面那棵树的 in/out 时间戳:[in[1]=2, out[1]=5] 和 [in[2]=6, out[2]=11]。判断「1 是不是 3 的祖先」「2 是不是 3 的祖先」?

(1 分)
原创教学题 | 考点:区间包含判祖先
第 84 题 单选 未作答

n=104n = 10^4 个顶点的稀疏图,为什么不用邻接矩阵存储和遍历?

(1 分)
原创教学题 | 考点:邻接矩阵大图开销
第 85 题 单选 未作答

大图的 DFS 遍历,为什么用显式栈(手写栈)替代递归,可以避免爆栈?对比两种实现:

01#include <iostream>
02#include <vector>
03using namespace std;
04vector<int> adj[105];
05bool vis[105];
06void dfs(int u) {
07    vis[u] = true;
08    for (int v : adj[u])
09        if (!vis[v]) dfs(v);
10}
11void dfs_stack(int start) {
12    vector<int> st;
13    st.push_back(start);
14    vis[start] = true;
15    while (!st.empty()) {
16        int u = st.back();
17        st.pop_back();
18        for (int v : adj[u])
19            if (!vis[v]) {
20                vis[v] = true;
21                st.push_back(v);
22            }
23    }
24}

(1 分)
原创教学题 | 考点:显式栈 DFS 不占递归栈
陆

易错与综合(真题形态)

16 计分点 · 每点 1 分
第 86 题 单选 未作答

下面的 BFS 求各点到起点的距离。它有一个严重错误:dist 数组被初始化成了 0(而不是 -1)。运行后会发生什么?

01queue<int> q;
02for (int i = 0; i < n; i++) dist[i] = 0;
03dist[start] = 0;
04q.push(start);
05while (!q.empty()) {
06    int u = q.front(); q.pop();
07    for (int v : adj[u])
08        if (dist[v] == -1) {
09            dist[v] = dist[u] + 1;
10            q.push(v);
11        }
12}

(1 分)
原创教学题 | 考点:找错:dist 数组初值 0
第 87 题 单选 未作答

阅读下面的程序:图有 5 个点,边为 (0,1)、(0,2)、(1,3)、(2,4),从 0 开始 DFS,输出什么顺序?

01#include <iostream>
02#include <vector>
03using namespace std;
04vector<int> adj[5];
05bool vis[5];
06void dfs(int u) {
07    cout << u << " ";
08    vis[u] = true;
09    for (int v : adj[u])
10        if (!vis[v]) dfs(v);
11}
12int main() {
13    adj[0].push_back(1); adj[0].push_back(2);
14    adj[1].push_back(0); adj[1].push_back(3);
15    adj[2].push_back(0); adj[2].push_back(4);
16    dfs(0);
17    return 0;
18}

(1 分)
原创教学题 | 考点:阅读 DFS 程序:遍历序追踪
第 88 题 单选 未作答

老师推荐读图遍历程序时:先把邻接表写出来,再在纸上画一棵「遍历树」(每个点第一次被访问时从哪个点进入的),最后沿树推访问序。为什么这个方法有效?

(1 分)
原创教学题 | 考点:读图遍历程序:为什么画遍历树
第 89~92 题 阅读程序 (共 4 分) 未作答

题目:以顶点 0 为起点,对下面的无向图做深度优先遍历。顶点依次编号 0、1、2、3、4。问 1、2、3、4 四个点中,有多少个可能成为「最后一个被访问到的点」?本题数据:边为 (0,1)、(0,2)、(1,3)、(2,4)(这是一棵以 0 为根、1 和 2 各带一个叶子的树,形状如下)。

      0
     / \
    1   2
   /     \
  3       4

01#include <iostream>
02#include <vector>
03using namespace std;
04vector<int> adj[5];
05bool vis[5];
06void addEdge(int u, int v) {
07    adj[u].push_back(v);
08    adj[v].push_back(u);
09}
10void dfs(int u) {
11    cout << u << " ";
12    vis[u] = true;
13    for (int v : adj[u])
14        if (!vis[v]) dfs(v);
15}
16int main() {
17    addEdge(0, 1); addEdge(0, 2);
18    addEdge(1, 3); addEdge(2, 4);
19    dfs(0);
20    return 0;
21}

89.

本题第一问:按上面代码的加边顺序,DFS 输出什么?

(1 分)
90.

本题第二问:点 1 有没有可能成为最后一个访问到的点?为什么?

(1 分)
91.

本题第三问:如果调整加边顺序为 addEdge(0, 2); addEdge(0, 1);(其余不变),最后一个访问的点是谁?

(1 分)
92.

本题第四问:本题的图里,1、2、3、4 中总共有多少个点可能成为最后一个访问到的点?

(1 分)
原创教学题 | 典型代码:DFS 最后一个访问点(真题形态)
第 93 题 单选 未作答

用邻接矩阵存图(a[u][v] == 1 表示有边),DFS 时逐列扫描找邻居:for (v = 0; v < n; v++) if (a[u][v] && !vis[v]) dfs(v);。4 个点的图,边为 0-1、0-2、1-3,从 0 开始,输出什么顺序?

01int a[4][4] = {0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0};
02bool vis[4];
03void dfs(int u) {
04    cout << u << " ";
05    vis[u] = true;
06    for (int v = 0; v < 4; v++)
07        if (a[u][v] && !vis[v]) dfs(v);
08}

(1 分)
原创教学题 | 考点:邻接矩阵 DFS 输出追踪
第 94 题 单选 未作答

同一张图(边 0-1、0-2、0-3),用两段代码分别 DFS。甲先 addEdge(0,1)、再 (0,2)、再 (0,3);乙先 (0,3)、再 (0,2)、再 (0,1)。甲的输出是 0 1 2 3,乙的输出是什么?

(1 分)
原创教学题 | 考点:两段代码对比:加边顺序不同
第 95 题 单选 未作答

下面的 BFS 没有判重,无条件把邻居全部入队。对「0-1、0-2、1-2」这样一个三角形运行,会发生什么?

01queue<int> q;
02q.push(0);
03while (!q.empty()) {
04    int u = q.front(); q.pop();
05    cout << u << " ";
06    for (int v : adj[u]) q.push(v);
07}

(1 分)
原创教学题 | 考点:找错:BFS 无条件入队
第 96 题 单选 未作答

BFS 用队列实现:新发现的点从队尾进入,处理时从队首取出。为什么这样得到的出队顺序天然就是「按层」的(第 0 层、第 1 层、第 2 层……)?看下面的 BFS 骨架:

01#include <iostream>
02#include <queue>
03#include <vector>
04using namespace std;
05vector<int> adj[105];
06int dist[105];
07void bfs(int s) {
08    for (int i = 0; i < 105; i++) dist[i] = -1;
09    dist[s] = 0;
10    queue<int> q;
11    q.push(s);
12    while (!q.empty()) {
13        int u = q.front();
14        q.pop();
15        for (int v : adj[u])
16            if (dist[v] == -1) {
17                dist[v] = dist[u] + 1;
18                q.push(v);
19            }
20    }
21}

(1 分)
原创教学题 | 考点:BFS 出队顺序为什么是层序
第 97 题 单选 未作答

阅读下面的程序:图有 5 个点,边为 0-1、2-3(点 4 孤立)。程序输出什么?

01#include <iostream>
02#include <vector>
03using namespace std;
04vector<int> adj[5];
05bool vis[5];
06int cnt;
07void dfs(int u) {
08    vis[u] = true;
09    cnt++;
10    for (int v : adj[u])
11        if (!vis[v]) dfs(v);
12}
13int main() {
14    adj[0].push_back(1); adj[1].push_back(0);
15    adj[2].push_back(3); adj[3].push_back(2);
16    int ans = 0;
17    for (int i = 0; i < 5; i++) {
18        if (!vis[i]) {
19            cnt = 0;
20            dfs(i);
21            if (cnt > ans) ans = cnt;
22        }
23    }
24    cout << ans << endl;
25    return 0;
26}

(1 分)
原创教学题 | 考点:阅读 DFS 程序:分量大小统计
第 98 题 单选 未作答

阅读图的遍历程序时,第一步为什么是「把邻接表/矩阵画出来」,而不是直接盯着代码模拟?

(1 分)
原创教学题 | 考点:为什么先画邻接表再读程序
第 99 题 单选 未作答

有向图的边为 0→1、0→2、1→3、2→4。从 0 出发沿出边 DFS(只沿箭头方向走),输出什么顺序?

(1 分)
原创教学题 | 考点:有向图 DFS 遍历序
第 100 题 单选 未作答

真题的「阅读程序」题里,图的规模常常很大(几十上百个点),不可能把完整遍历序写出来。老师建议先在纸上跑一遍小数据(比如 3~4 个点),再找规律。为什么?

(1 分)
原创教学题 | 考点:为什么读程序要先模拟小数据
第 101 题 单选 未作答

「翻转被包围的 0」的代码:先把所有边界上的 0 入队,再 BFS 标记所有可达的 0,最后把没标记的 0 翻转。若写代码时漏了把边界 0 入队(队列初始为空),会发生什么?

(1 分)
原创教学题 | 考点:找错:反向泛洪漏入队边界
柒

真题检验 · 历年真题(原样罗列)

2 道真题 | 每题 1 分
第 102 题 单选 未作答

以 a 为起点,对右边的无向图进行深度优先遍历,则 b、c、d、e 四个点中有可能作为最后一个遍历到的点的个数为( )。

(1 分)
CSP-J 2021 · 单选 第14题 | 知识点 图论遍历与泛洪
第 103~107 题 完善程序 (共 5 分) 未作答

试补全程序。

1  #include <bits/stdc++.h>
2  using namespace std;
3 
4  const int ROWS = 8;
5  const int COLS = 8;
6 
7  struct Point {
8    int r, c;
9    Point(int r, int c) : r(r), c(c) {}
10  };
11 
12  bool is_valid(char image[ROWS][COLS], Point pt,
13                  int prev_color, int new_color) {
14    int r = pt.r;
15    int c = pt.c;
16    return (0 <= r && r < ROWS && 0 <= c && c < COLS &&
17            ① && image[r][c] != new_color);
18  }
19 
20  void flood_fill(char image[ROWS][COLS], Point cur, int new_color) {
21    queue<Point> queue;
22    queue.push(cur);
23 
24    int prev_color = image[cur.r][cur.c];
25    ②;
26 
27    while (!queue.empty()) {
28      Point pt = queue.front();
29      queue.pop();
30 
31      Point points[4] = {③, Point(pt.r - 1, pt.c),
32                          Point(pt.r, pt.c + 1), Point(pt.r, pt.c - 1)};
33      for (auto p : points) {
34        if (is_valid(image, p, prev_color, new_color)) {
35          ④;
36          ⑤;
37        }
38      }
39    }
40  }
41 
42  int main() {
43    char image[ROWS][COLS] = {{'g', 'g', 'g', 'g', 'g', 'g', 'g', 'g'},
44                                {'g', 'g', 'g', 'g', 'g', 'g', 'r', 'r'},
45                                {'g', 'r', 'r', 'g', 'g', 'r', 'g', 'g'},
46                                {'g', 'b', 'b', 'b', 'b', 'r', 'g', 'r'},
47                                {'g', 'g', 'g', 'b', 'b', 'r', 'g', 'r'},
48                                {'g', 'g', 'g', 'b', 'b', 'b', 'b', 'r'},
49                                {'g', 'g', 'g', 'g', 'g', 'b', 'g', 'g'},
50                               {'g', 'g', 'g', 'g', 'g', 'b', 'b', 'g'}};
51 
52    Point cur(4, 4);
53    char new_color = 'y';
54 
55    flood_fill(image, cur, new_color);
56 
57    for (int r = 0; r < ROWS; r++) {
58      for (int c = 0; c < COLS; c++) {
59        cout << image[r][c] << " ";
60      }
61      cout << endl;
62    }
63    // 输出:
64    // g g g g g g g g
65    // g g g g g g r r
66    // g r r g g r g g
67    // g y y y y r g r
68    // g g g y y r g r
69    // g g g y y y y r
70    // g g g g g y g g
71    // g g g g g y y g
72 
73    return 0;
74  }

103.

①处应填( )

(1 分)
104.

②处应填( )

(1 分)
105.

③处应填( )

(1 分)
106.

④处应填( )

(1 分)
107.

⑤处应填( )

(1 分)
CSP-J 2022 · 完善程序 第40-40-44题 | 知识点 图论遍历与泛洪