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

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;
}

功能分析

  1. 读入有向图,统计每个结点的入度和出度。
  2. 合法起点为入度 (0) 的结点,合法终点为出度 (0) 的结点。
  3. 对每个结点 (u),删除 (u) 后从所有合法起点开始 BFS。
  4. BFS 过程中若到达任意合法终点,说明存在一条不经过 (u) 的合法路径,(u) 不是必经点。
  5. 若 BFS 无法到达任何合法终点,说明所有合法路径都经过 u,u 是必经点。
  6. 最后按编号从小到大输出所有必经点。

各种学习资料,助力大家一站式学习和提升!!!

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;
}
相关推荐
码匠许师傅1 小时前
【C++三方组件】Asio 上篇:回调式 TCP 客户端与服务端
开发语言·c++·tcp/ip
沐欣工作室_lvyiyi1 小时前
家庭水资源节能系统设计(论文+源码)
c++·python·云平台·单片机毕业设计·单片机仿真·电子信息工程毕业设计·机械电子工程
Yyyyyy~2 小时前
[c++]数组
c++
知识分享小能手2 小时前
C++ 学习教程,从入门到精通,C++内存模型和名称空间 — 详细知识点总结(9)
开发语言·c++·学习
2601_962218612 小时前
c++中多重继承与虚继承的实现
开发语言·c++
沫璃染墨2 小时前
《Qt从零入门系列(十一):Qt事件机制详解——从QEvent到鼠标、键盘与定时器事件》
c++·qt·ui·硬件工程·交互·个人开发·qt5
2601_962218613 小时前
C++中分配器allocator的实现
开发语言·c++
单片机设计·······3 小时前
【dz-1162】汽车前玻璃雨刮器设计
c++·单片机·嵌入式硬件
AI智讯中枢3 小时前
高性能 C++ 实战 (五):perf+FlameGraph 火焰图生产实战,精准定位 CPU / 缓存 / 锁瓶颈,避坑 + 完整实操案例
linux·c++·性能调优·性能分析·perf·flamegraph·火焰图