树与图论

树与图论

1 基础概念 & 性质

1.1 图

  • 图由顶点和边组成,一条边连接两个顶点
  • 自环:一条边链接相同的两个顶点
  • 重边:两条边所链接的两个顶点都相同
  • 点和边都可以带权,称为点权边权
  • 点的度数(有向图分入度出度
  • 稠密图\(m\) ~ \(O(n^2)\)\(m\) 边数,\(n\) 点数)
  • 稀疏图\(m\) ~ \(O(n)\)
  • ~ 表示接近于

1.2 树

  • 所有顶点之间相互连通且不存在环
  • 一棵树有 \(n\) 个顶点,\(n-1\) 条边
  • 两点之间仅存在一条简单路径

1.3 存图方式

1.3.1 邻接矩阵

  • \(G[i][j]=w\) 存储 \((i,j)\) 之间有一条边权\(w\) 的边
  • 适用于稠密图

1.3.2 邻接表

  • \(vector\) 存储每个结点的所有出边
  • 适用于稀疏图

1.4 树的性质

1.4.1 二叉树的遍历

  • 前序遍历:左右
  • 中序遍历:左
  • 后序遍历:左右
  • 给定二叉树,求遍历顺序时模仿递归函数进行模拟
  • 根据分治思想,给定 中序遍历(必须有) 和其他一个遍历,可以倒推出最后一种遍历或原二叉树

1.4.2 树的直径

  • 树上的一条路径,满足其边权之和最大
  • 如果边权均非负的求法
    • 随意选择一个结点进行 DFS,找到距离它最远的结点 \(s\)
    • \(s\) 开始进行 DFS,找到距离它最远的结点 \(t\)
    • \(s \rightarrow t\) 即为树的直径
  • 例题:P1099
    • 选择的路径长度越长,偏心距不会上升
    • 使用双指针维护尽可能长的路径

2 生成树

2.1 概念

  • \(n\) 个结点,\(m\) 条边的图上,选择其中 \(n-1\) 条边构成一棵树,使得所有结点相互连通
  • 当生成树所有边权之和最小时,称作最小生成树
  • 当生成树所有边权之和最大时,称作最大生成树

2.2 构建生成树

  • 一开始有 \(n\) 个独立的结点,每个结点自身构成一个连通块
  • 每次插入一条边就会将两个连通块合并
  • 判断是否联通,使用并查集确认每次是将两个连通块合并

2.3 构建最小生成树

  • 贪心策略(Kruskal 算法):将边权从小到大排序,依次考虑是否选取
    1. 将边权从小到大排序
    2. 依次扫描每一条边 \((u,v,w)\)
    3. 如果 \(u,v\) 属于同一连通块,那么舍弃
    4. 如果 \(u,v\) 属于不同连通块,那么连边,并查集合并
    • 时间复杂度 \(O(m \log m)\)
    • 从边出发,适用于稀疏图
  • 搜索策略(Prim 算法)
    1. 建立 \(dis\) 数组表示当前生成树中的点到该点的最短边长
    2. 首先加入 1 号点
    3. 更新 \(dis\) 数组
    4. \(dis\) 数组中选择最小的且未加入生成树的结点加入生成树
    5. 如果没有构建完成,回到第三步
    • 时间复杂度 \(O(n^2)\)
    • 从点出发,适用于稠密图
  • 例题:P2872
    • 结点之间两两连边,边权为点之间的距离
    • 对于一定要选的 \(m\) 条边,将边权设置为 \(0\) 即可
    • AC 记录
  • 例题:P2330
    • 让最大边权最小:考察 Kruskal 算法本质
    • 学名:最小瓶颈生成树

3 最短路问题

3.1 单源最短路

3.1.1 边权非负 - Dijkstra 算法

3.1.1.1 问题描述

在一张有 \(n\) 个结点 \(m\) 条边的带非负边权的图上,求从起点 \(s\) 到其他结点的最短路径

3.1.1.2 不使用堆优化(稠密图)
  • 定义 \(dis\) 数组表示每一个结点到该结点的最短距离
  • 每次从未标记的节点中选择距离出发点最近的结点,标记,收录到最优结点集合中
  • 计算刚加入结点和临近结点的距离,如果新的距离更优,那么更新答案
  • 时间复杂度 \(O(n^2)\) 适用于稠密
3.1.1.3 使用堆优化(稀疏图)
  • 考虑到优化前的第二步可以使用优先队列优化(小根堆)
  • 定义小根堆的方式
    • 重载运算符
    • priority_queue<int, vector<int>, greater<int>()> q;
  • 时间复杂度 \(O(m \log m)\) 适用于稀疏

3.1.2 负权图

3.1.2.1 问题描述

基本同上,只是边权可以为负

3.1.2.2 Bellman-ford 算法流程
  • 定义 \(dis\) 数组表示每一个结点到该结点的最短距离
  • 对每一条边进行松弛操作
  • 重复上一步骤 \(n-1\)
  • 时间复杂度 \(O(nm)\)
3.1.2.3 队列优化(SPFA 算法)
  • 定义 \(dis\) 数组表示每一个结点到该结点的最短距离
  • 从队列中取出点 \(u\),松弛所有与 \(u\) 相连的边 \(u,v\),若松弛成功且 \(v\) 不在队列中,则将 \(v\) 加入队列中
  • 重复上述操作直至队列为空
  • 需要注意,SPFA 算法的时间复杂度为 \(O(nm)\)

3.1.3 没有最短路的情况(负环)

  • 如果图上存在一个回路(环),它的边权之和为负数,那么无法求得最短路
  • 判断是否存在负环:最短路经过不少于 \(n\) 条边
  • 不要把 SPFA 的队列改成栈(奇技淫巧)
  • 例题:P1629
    • 从邮局跑 Dijkstra 求得邮局到所有结点的最短路
    • 从其他结点返回邮局:反向建立所有边的最短路

3.2 多源最短路(出发点不固定)

3.2.1 问题描述

  • 求出每一对结点 \((i,j)\) 之间的距离

3.2.2 Floyd 算法

  • 基于动态规划思想
  • 定义 \(f[k][i][j]\) 表示经过若干编号不超过 \(k\) 的结点,从结点 \(i\) 到结点 \(j\) 的最短路
  • 转移
    • 经过若干个编号不超过 \(k-1\) 的结点从 \(i\)\(j\)
    • 从结点 \(i\)\(k\) 再到 \(j\)
    • \[f[k][i][j]=\min(f[k-1][i][j],f[k-1][i][k]+f[k-1][k][j]) \]

  • 枚举顺序:\(k\rightarrow i\rightarrow j\)
  • 优化掉数组第一维:$$f[i][j]=\min(f[i][j],f[i][k]+f[k][j])$$
  • 例题:P6464
    • 使用 Floyd 求出全源最短路
    • 枚举一对结点 \((i,j)\) 造传送门(边权为 \(0\) 的边),此时结点 \(a,b\) 的距离可以表示为 $$d(a,b)=\min(d(a,b),d(a,i)+d(j,b),d(a,j)+d(i,b))$$
    • 时间复杂度 \(O(n^4)\)
  • 例题:P8794
    • 二分 + Floyd
    • 随着时间的流逝,指标 \(P\) 一定单调不增,所以可以用二分答案来计算日期

4 拓扑排序

4.1 问题描述

对有向无环图(\(DAG\))上的所有结点排序,满足排在前面的结点不能依赖与排在后面的结点

4.2 一般方法

  • 将所有入度为 \(0\) 的结点插入队列
  • 取出队首元素 \(u\),将其计入答案
  • 删除与 \(u\) 相连的所有边 \((u,v)\),若此时 \(v\) 的入度为 \(0\) 则将其插入队列
  • 若队列非空则继续进行第二第三步

4.3 模板题

  • 例题:P1113
    • 使用动态规划思想,设 \(f[i]\) 表示完成第 \(i\) 项任务需要耗费的时间
    • 可得 \(f[j] = \max(f[j], f[i] + len[j])\),其中 \(j\)\(i\) 的后继任务
    • 时间复杂度 \(O(n + m)\)
    • AC 记录
  • 例题:P2017
    • 把一张无向图标定方向,使其成为有向无环图(\(DAG\)
posted @ 2023-08-12 11:16  SuperUser777  阅读(61)  评论(0)    收藏  举报