2026年9月GESP真题及题解(C++七级):必经之路

题目描述
给定一张有 n n n 个结点 m m m 条边的有向图 G G G, G G G 中的结点依次以 1 , 2 , ... , n 1,2,\ldots,n 1,2,...,n 编号。第 i i i 条边( 1 ≤ i ≤ m 1\le i\le m 1≤i≤m)从结点 u i u_i ui 指向结点 v i v_i vi。
G G G 中任一入度为 0 0 0 的结点可以作为合法起点,任一出度为 0 0 0 的结点可以作为合法终点。
如果 G G G 中所有可能的从合法起点到合法终点的路径都会经过结点 u u u,则称 u u u 是必经点。注意必经点可以为合法起点或合法终点。
请你求出 G G G 中所有必经点的编号。
例如,在下图中合法起点有点 1 1 1 与点 2 2 2,合法终点有点 7 7 7 与点 8 8 8。
text
(1) (5)---->(7)
\ ^ \ ^
v / v /
(3) / (6)
^ \ / \
/ v / v
(2)---->(4) (8)
所有合法起点到合法终点的路径为:
- 1 → 3 → 4 → 5 → 7 1\to 3\to 4\to 5\to 7 1→3→4→5→7
- 1 → 3 → 4 → 5 → 6 → 7 1\to 3\to 4\to 5\to 6\to 7 1→3→4→5→6→7
- 1 → 3 → 4 → 5 → 6 → 8 1\to 3\to 4\to 5\to 6\to 8 1→3→4→5→6→8
- 2 → 3 → 4 → 5 → 7 2\to 3\to 4\to 5\to 7 2→3→4→5→7
- 2 → 3 → 4 → 5 → 6 → 7 2\to 3\to 4\to 5\to 6\to 7 2→3→4→5→6→7
- 2 → 3 → 4 → 5 → 6 → 8 2\to 3\to 4\to 5\to 6\to 8 2→3→4→5→6→8
- 2 → 4 → 5 → 7 2\to 4\to 5\to 7 2→4→5→7
- 2 → 4 → 5 → 6 → 7 2\to 4\to 5\to 6\to 7 2→4→5→6→7
- 2 → 4 → 5 → 6 → 8 2\to 4\to 5\to 6\to 8 2→4→5→6→8
因此必经点有两个,编号分别为 4 , 5 4,5 4,5。
输入格式
第一行,两个正整数 n , m n,m n,m,表示有向图 G G G 中的结点数与边数。
接下来 m m m 行,每行两个正整数 u i , v i u_i,v_i ui,vi,表示一条从结点 u i u_i ui 指向结点 v i v_i vi 的有向边。
保证 G G G 中至少有一个合法起点,至少有一个合法终点,且至少存在一条从一个合法起点到一个合法终点路径,同时不存在孤立点(即出度和入度都为 0 0 0 的点)。
输出格式
第一行,一个整数,表示必经点的数量 k k k。
如果存在必经点,则第二行从小到大输出 G G G 中所有必经点的编号。
输入输出样例 1
输入 1
8 9
1 3
2 3
3 4
4 5
5 6
6 7
6 8
2 4
5 7
输出 1
2
4 5
输入输出样例 2
输入 2
8 9
1 3
2 3
3 4
4 5
5 6
6 7
6 8
2 5
4 7
输出 2
0
说明/提示
对于 40 % 40\% 40% 的测试点,保证 1 ≤ n ≤ 100 1\le n\le 100 1≤n≤100, 1 ≤ m ≤ 200 1\le m\le 200 1≤m≤200。
对于所有测试点,保证 1 ≤ n ≤ 1000 1\le n\le 1000 1≤n≤1000, 1 ≤ m ≤ 2000 1\le m\le 2000 1≤m≤2000。保证 G G G 中至少有一个合法起点,至少有一个合法终点,且至少存在一条从一个合法起点到一个合法终点路径,同时不存在孤立点(即出度和入度都为 0 0 0 的点)。
思路分析
本题要求找出所有从任意合法起点到任意合法终点的路径都必须经过的结点。
- 合法起点:入度为 (0) 的结点。
- 合法终点:出度为 (0) 的结点。
判断结点 (u) 是否为必经点,可以转化为:
- 如果删除结点 (u) 后,仍然存在某条从合法起点到合法终点的路径,那么这条路径在原图中不经过 (u),所以 (u) 不是必经点。
- 如果删除结点 (u) 后,不存在任何从合法起点到合法终点的路径,那么原图中所有合法路径都必然经过 (u),所以 (u) 是必经点。
因此可以枚举每个结点 (u),在删除 (u) 的图上,从所有合法起点(排除 u)出发做 BFS,看能否到达任意合法终点(排除 u)。
若不能到达,则 (u) 是必经点。
数据范围 n ≤ 1000 n \le 1000 n≤1000, m ≤ 2000 m \le 2000 m≤2000,每次 BFS 复杂度 O(n+m),总复杂度 O(n(n+m))。
代码实现
cpp
#include<bits/stdc++.h>
using namespace std;
int n,m;//结点数和边数
vector<int> g[1005];//邻接表
int d1[1005],d2[1005];//d1入度,d2出度
bool f(int x){//检查删除x后是否还有合法路径
vector<int> q(n+1);//BFS队列
vector<char> v(n+1,0);//访问标记
int h=0,t=0;//队头h,队尾t
for(int i=1;i<=n;i++){//枚举所有原合法起点
if(d1[i]==0&&i!=x){//入度为0且不是删除点
v[i]=1;//标记起点
q[t++]=i;//起点入队
if(d2[i]==0&&i!=x)return 1;//起点也是合法终点
}
}
while(h<t){//BFS
int a=q[h++];//取出队头
if(d2[a]==0&&a!=x)return 1;//到达原合法终点
for(int i=0;i<(int)g[a].size();i++){//遍历出边
int b=g[a][i];//出边终点
if(b==x||v[b])continue;//跳过删除点和已访问点
v[b]=1;//标记访问
q[t++]=b;//入队
if(d2[b]==0&&b!=x)return 1;//到达原合法终点
}
}
return 0;//不存在不经过x的路径
}
int main(){
cin>>n>>m;
for(int i=0;i<m;i++){//读入m条边
int u,v;//边起点和终点
cin>>u>>v;//读入边
g[u].push_back(v);//加入邻接表
d2[u]++;//u出度加1
d1[v]++;//v入度加1
}
vector<int> r;//存储必经点
for(int i=1;i<=n;i++){//枚举每个结点
if(!f(i))r.push_back(i);//删除后无路径则i必经
}
cout<<r.size()<<'\n';//输出必经点数量
if(!r.empty()){//存在必经点
for(int i=0;i<(int)r.size();i++){//输出编号
if(i)cout<<' ';//非第一个前加空格
cout<<r[i];//输出编号
}
cout<<'\n';//换行
}
return 0;
}
功能分析
- 读入有向图,统计每个结点的入度和出度。
- 合法起点为入度 (0) 的结点,合法终点为出度 (0) 的结点。
- 对每个结点 (u),删除 (u) 后从所有合法起点开始 BFS。
- BFS 过程中若到达任意合法终点,说明存在一条不经过 (u) 的合法路径,(u) 不是必经点。
- 若 BFS 无法到达任何合法终点,说明所有合法路径都经过 u,u 是必经点。
- 最后按编号从小到大输出所有必经点。
各种学习资料,助力大家一站式学习和提升!!!
cpp
#include<bits/stdc++.h>
using namespace std;
int main(){
cout<<"########## 一站式掌握信奥赛知识! ##########";
cout<<"############# 冲刺信奥赛拿奖! #############";
cout<<"###### 课程购买后永久学习,不受限制! ######";
return 0;
}
【秘籍汇总】(完整csp信奥赛C++学习资料):
1、csp/信奥赛C++,完整信奥赛系列课程(永久学习):
https://edu.csdn.net/lecturer/7901 点击跳转

2、CSP信奥赛C++竞赛拿奖视频课:
https://edu.csdn.net/course/detail/40437 点击跳转

https://edu.csdn.net/course/detail/41081 点击跳转

3、csp信奥赛高频考点知识详解及案例实践:
CSP信奥赛C++动态规划:
https://blog.csdn.net/weixin_66461496/category_13096895.html点击跳转
CSP信奥赛C++标准模板库STL:
https://blog.csdn.net/weixin_66461496/category_13108077.html 点击跳转
信奥赛C++提高组csp-s知识详解及案例实践:
https://blog.csdn.net/weixin_66461496/category_13113932.html 点击跳转
4、csp信奥赛冲刺一等奖有效刷题题解:
信奥赛C++普及组CSP-J一等奖通关刷题题单及题解:
https://blog.csdn.net/weixin_66461496/category_12673810.html 点击跳转
信奥赛C++普及组csp-j初赛&复赛真题题解(持续更新): https://blog.csdn.net/weixin_66461496/category_12808781.html 点击跳转
信奥赛C++提高组csp-s初赛&复赛真题题解(持续更新):
https://blog.csdn.net/weixin_66461496/category_13125089.html 点击跳转
5、GESP C++考级真题题解:

GESP(C++ 一级+二级+三级)真题题解(持续更新):https://blog.csdn.net/weixin_66461496/category_12858102.html 点击跳转

GESP(C++ 四级+五级+六级)真题题解(持续更新):https://blog.csdn.net/weixin_66461496/category_12869848.html 点击跳转

GESP(C++ 七级+八级)真题题解(持续更新):
https://blog.csdn.net/weixin_66461496/category_13117178.html 点击跳转
· 文末祝福 ·
cpp
#include<bits/stdc++.h>
using namespace std;
int main(){
cout<<"跟着王老师一起学习信奥赛C++";
cout<<" 成就更好的自己! ";
cout<<" csp信奥赛一等奖属于你! ";
return 0;
}
