图论基础
图是若干个顶点和若干条边构成的数据结构,顶点是实际对象的抽象,边是对象之间关系的抽象。可以将图形式化表示为二元组
图可以分为无向图(undirected graph)、有向图(directed graph)、混合图(mixed graph)。无向图的边集
在无向图中,若任意两个顶点之间都存在边,则该无向图称为完全无向图。
在无向图中,若点
与顶点相关联的边的数目或者弧的数目称为该顶点的度。在无向图中,顶点的度就是其关联的边的数目。在有向图中,由于与顶点关联的弧具有方向性,因此要区分顶点的入度和出度。入度指以该顶点为弧头的弧的数目,而出度指以该顶点为弧尾的弧的数目,入度与出度之和是该顶点的度。
答案
6 号结点,度为 4
例题:P5318 【深基18.例3】查找文献
小 K 喜欢翻看洛谷博客获取知识。每篇文章可能会有若干(也有可能没有)参考文献的链接指向别的博客文章。小 K 求知欲旺盛,如果他看了某篇文章,那么他一定会去看这篇文章的参考文献(如果他之前已经看过这篇参考文献就不用再看它了)。
假设洛谷博客里面一共有篇文章(编号为 到 )以及 条参考文献引用关系。目前小 K 已经打开了编号为 的一篇文章,输出 DFS、BFS 两种遍历方式下看文章的顺序(当有多篇参考文章时,先看编号小的)。
#include <cstdio> #include <algorithm> #include <vector> #include <queue> using std::sort; using std::vector; using std::queue; using std::pair; using Edge = pair<int, int>; const int N = 1e5 + 5; const int M = 1e6 + 5; Edge e[M]; vector<int> g[N]; bool vis[N]; void dfs(int u) { vis[u] = true; printf("%d ", u); for (int v : g[u]) { if (!vis[v]) { dfs(v); } } } int main() { int n, m; scanf("%d%d", &n, &m); for (int i = 1; i <= m; i++) { int x, y; scanf("%d%d", &x, &y); e[i] = {x, y}; } sort(e + 1, e + m + 1); // 将输入的边排序后再真正建图 for (int i = 1; i <= m; i++) { int x = e[i].first, y = e[i].second; g[x].push_back(y); } dfs(1); printf("\n"); for (int i = 1; i <= n; i++) vis[i] = false; // DFS后BFS前清空标记数组 queue<int> q; q.push(1); vis[1] = true; while (!q.empty()) { int u = q.front(); printf("%d ", u); q.pop(); for (int v : g[u]) { if (!vis[v]) { q.push(v); vis[v] = true; } } } printf("\n"); return 0; }
程序阅读题:
#include <iostream> #include <vector> #include <queue> using namespace std; const int MAXN = 200001; int main() { int n, m, l, r, w; cin >> n >> m; vector <int> dist(MAXN, -1); vector <bool> vis(MAXN, false); vector <vector <pair<int, int> > > go(MAXN); for (int i = 1; i <= m; i++) { cin >> l >> r >> w; go[l].push_back(make_pair(r + 1, w)); go[r + 1].push_back(make_pair(l, -w)); } queue <int> q; dist[1] = 0; vis[1] = true; q.push(1); while (!q.empty()) { int x = q.front(); q.pop(); for (auto i : go[x]) { if (!vis[i.first]) { vis[i.first] = true; dist[i.first] = dist[x] + i.second; q.push(i.first); } } } if (dist[n + 1] == -1) cout << "sorry" << endl; else cout << dist[n + 1] << endl; return 0; }
假设输入的
判断题
交换程序的第
答案
正确。第
输入的
答案
错误。数组的大小设定为
在程序的第
答案
错误。进入队列的条件是 !vis[i.first]
,一旦进入队列后 vis[i.first]=true
,因此不可能重复进队。
单选题
当输入的
A. 1 / B. y-x / C. y-x+1 / D. y-x+2
答案
D。这个程序相当于建图后从点
当输入的 sorry
?
A.
答案
A。如果 dist
数组的作用是在宽搜过程中更新从 dist[n+1]
等于 -1
)则输出 sorry
。因此要使得
当输入为 5 3 1 3 4 3 4 2 4 5 3
时,输出为?
A. 4 / B. 5 / C. 6 / D. 7
答案
D。根据输入数据建的图如下所示:
P2097
#include <cstdio> #include <vector> #include <algorithm> #include <queue> using std::sort; using std::vector; using std::queue; const int N = 1e5+5; vector<int> g[N]; bool vis[N]; // 标记i是否被搜到过 void dfs(int u) { vis[u]=true; for (int v : g[u]) { // u->v if (!vis[v]) { dfs(v); } } } int main() { int n,m; scanf("%d%d",&n,&m); for (int i=1;i<=m;i++) { int u,v; scanf("%d%d",&u,&v); g[u].push_back(v); g[v].push_back(u); } int ans=0; for (int i=1;i<=n;i++) { if (!vis[i]) { dfs(i); ans++; } } printf("%d\n",ans); return 0; }
P3916
#include <cstdio> #include <vector> #include <algorithm> #include <queue> using std::sort; using std::vector; using std::queue; const int N = 1e5+5; int ans[N]; vector<int> g[N]; bool vis[N]; // 标记i是否被搜到过 void dfs(int u, int source) { // 这个搜索是哪个大编号点发起的 vis[u]=true; ans[u]=source; for (int v : g[u]) { // u->v if (!vis[v]) { dfs(v, source); } } } int main() { int n,m; scanf("%d%d",&n,&m); for (int i=1;i<=m;i++) { int u,v; scanf("%d%d",&u,&v); g[v].push_back(u); // 反向建图 } for (int i=n;i>=1;i--) { // 优先从编号大的点发起搜索 if (!vis[i]) { dfs(i, i); } } for (int i=1;i<=n;i++) printf("%d ", ans[i]); return 0; }
P2661
#include <cstdio> #include <algorithm> using std::min; const int N = 2e5+5; int t[N]; bool vis[N]; int ans[N]; // ans[i]表示从i出发最终会遇到的环的长度 int num[N]; // 递归过程中报数 void dfs(int u, int level) { // level是递归层数,报数 if (vis[u]) { if (ans[u]==0) { // 第一次找到这个环 ans[u]=level-num[u]; } return; } vis[u]=true; num[u]=level; dfs(t[u],level+1); // 回溯 ans[u]=ans[t[u]]; } int main() { int n; scanf("%d",&n); for (int i=1;i<=n;i++) scanf("%d",&t[i]); for (int i=1;i<=n;i++) { if (!vis[i]) { dfs(i,1); } } int r=n; for (int i=1;i<=n;i++) r=min(r,ans[i]); printf("%d\n",r); return 0; }
例题:P1144 最短路计数
解题思路
无权图最短路问题,可以用 BFS 解决。
设
if dis[u] + 1 < dis[v] ans[v] = ans[u] else ans[v] += ans[u]
初始化
参考代码
#include <cstdio> #include <vector> #include <queue> const int N = 1000005; const int MOD = 100003; std::vector<int> g[N]; int dis[N], ans[N]; int main() { int n, m; scanf("%d%d", &n, &m); for (int i = 1; i <= n; i++) dis[i] = n; for (int i = 1; i <= m; i++) { int x, y; scanf("%d%d", &x, &y); g[x].push_back(y); g[y].push_back(x); } std::queue<int> q; q.push(1); ans[1] = 1; dis[1] = 0; while (!q.empty()) { int u = q.front(); q.pop(); for (int v : g[u]) { if (dis[u] + 1 < dis[v]) { dis[v] = dis[u] + 1; q.push(v); ans[v] = ans[u]; } else if (dis[u] + 1 == dis[v]) { ans[v] = (ans[v] + ans[u]) % MOD; } } } for (int i = 1; i <= n; i++) printf("%d\n", ans[i]); return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?