图论

  经典的七桥问题:

  问怎样走能经过所有的桥并且每个桥只经过一次;

  

  开始并不知道第二个图是怎么到的第三个图,然后某zz是这么说的;

  

  所以就是这样了;

  然后又是经典的国际象棋问题:

  国际象棋棋盘为8*8期盼,但我们一般是要扩展到n*m的,然后就要知道,马在棋盘上是怎么走的呢?

  Δx=1,Δy=2或者Δx=2,Δy=1;当然可能我们一般理解或者做题的时候,是在格点上操作的,

  但应该要转化到格子上去理解和操作,如图:

  

  然后要说的就是,nm取值的影响了,首先nm都是奇数,则无法构成回路(不能回到原来的位置);

  当n==1时,则无法完成移动步数为2的情况,所以无解;

  当n==2时,

  

  很明显所能到达的点的列数必定与初始所在列数奇偶性相同;

  当n==4时,我们引入黑白染色,红蓝染色这个说法,

  规定如下棋盘: 

  

   很明显途中有四个类型的格子:红黑,红白,蓝黑,蓝白;

  那么它一次能到达的所有可能的格子中,没有一个会是与原来这个格子染色情况相同的,

  如图:

  

  这时我们可以把整个棋盘分成两半,

  一半的染色情况是:红+黑 和 蓝+白,另一半的染色情况是:红+白 和 蓝+黑;

  但我们可以发现这与上图是矛盾的,我们每次走一步的两种情况的可能性并不是1:1,

  所以不能遍历到整个图;

  ……更大的情况略;

一些图的基本概念  

  节 (结, 顶) 点 (node): 图的基本构成元素, 一般以小写字母表示 (u, v, ...).
  标号: 大部分图论算法是建立在有标号节点之上的.
  边 (edge): 节点对 (u,v). 可分为有向边 (有序节点对) 和无向边 (无序节点对),
  对于有向边, u 称作始点, v 称作终点.
  重边: 相同的边
  自环: (u,u)
  图 (graph): G = (V,E). 其中 V 是点集, E 是可重边集.

  各种图:
  无向图, 有向图, 混合图.
  简单图 (无重边和自环)
  完全图 (K n ), 竞赛图

  DAG:有向无环图;

  补充:完全图:边最多的简单图,可以理解为,任意一对节点之间都有边,像这样

  

  那么竞赛图呢?:就是一个完全图,但是每个边都赋有一个方向,像这样:

  

  权
  边权: w : E → R/N
  点权: w : V → R/N

  度  

  度:边与点的相遇;  
  度: d : V → N, 与某节点相邻的边数.
  正 (出) 度: 以某节点为始点的边数.
  负 (入) 度: 以某节点为终点的边数.

  有根树和无根树度的定义不同,(一个包含所有入度出度,,另一个,,,)

  度的性质:

  

  度为奇数的节点必为偶数个.(只对竞赛图)

  

图的存储

  

  

  例如上图

  邻接矩阵:a[i][j],(无向图a[j][i]) ,缺点:不能记重边;

  前向星:

  十字链表,如图:

  

  

查询指定边信息?


  邻接矩阵: O(1)
  前向星: O(E)/O(logE)
  邻接表/十字链表: O(E)/O(logE)

路径  

  有向路径: P = (e 1 ,...,e n ), 其中 e i 的终点是 e i+1 的始点.
  初等: 路径经过的边两两不同.
  简单: 路径经过的点两两不同 (除路径的始点与终点外).
  环: 路径的始点与终点相同.
  无向图上的路径: 存在一种每一条边分配方向的方案使得其成为一条有向图的路径.

自由树  

  自由树: 连通的无环无向图.
  森林: 树的集合.

  对于一棵自由树而言, 以下 6 条等价:
  1.G 是一棵自由树.
  2.G 中任何两顶点由唯一简单路径相连.
  3.G 是连通的, 但是从图中移除任意一条边得到的图均不连通.(都是桥)
  4.G 是连通的, 且 |E| = |V| - 1
  5.G 是无环的, 且 |E| = |V| - 1
  6.G 是无环的, 但若向 E 中添加任意一条边, 均会造成该图包含恰有一个环.

有根树与二叉树  

  有根树: 自由树 + 根.
  节点之间的关系: 父亲与儿子, (真) 祖先与 (真) 后代, 叶节点.
  子树: u 的所有后代的导出子图.
  定义在点集上的函数: 度, 深度, 大小.

  导出子图是什么?
  设V1是V的一个非空子集,以V1为顶点集,
  以两端点均在V1中的边的全体为边集的子图称为G的导出子图.

  

二叉树:左子树,右子树

  一些性质:  

  任何非空二叉树中,度为2的节点数比叶节点数少1.
  n个节点的二叉树高度至少为 logn(下取整)
  Kraft 不等式: 将二叉树 T 中每个深度为 d 的叶节点赋予权值 w(x) = 2-d,则∑w(x) ≤1.

树的存储  

  可以保存的信息:
  边信息
  父亲
  儿子
  大部分题会将树作为一个无向图来读入, 也有的题会告诉你某个点的父亲是谁, 保存哪些信息取决于你的需求.

图的遍历  

  BFS(breadth first search): 求图中两点间最短路径?
  (路径长度被定义为其拥有的边数)
  时间复杂度: O(V + E)
  BFS 生成树
  DFS(depth first search): DFS 生成树.
  边的分类:
  树边
  后向边(指向祖先的边)
  前向边(指向后代的边)
  横向边(既不指向祖先,也不指向后代的边)
  性质:
  括号化定理 ([vl,vr])
  无向图 G 不存在横向边

联通  

  连通: 若图G的任意两点间均存在路径,则称图G连通.
  子图: 若G'= (V',E') 满足 V'⊆V, E'⊆E, 则称 G'是G的子图.
  导出子图:若E'包含了G在 V'中的所有边,则称 G'是G关于 V 的导出子图.
  (无向图的) 极大连通子图 (连通支): 若 G'满足不存在 H,
  使得 G'是 H 的子图, H 是 G 的子图, 则称 G'是 G 的极大连通子图.

拓扑排序  

  在DAG(Directed Acyclic Graph,有向无环图)中求一个排列P,
  使得对于每个点v而言,若(u,v)存在,则u在P中在v之前.
  Kahn算法:不断寻找入度为0的点.
  基于DFS的算法:以vr排序.

欧拉路径: 经过所有边的简单路径.
欧拉回路: 经过所有边的简单环.

欧拉路径(环)  

  连通无向图:
  存在欧拉回路 等价于度为奇数的点有 0 个.
  存在欧拉路径 等价于度为奇数的点有 0 或 2 个.
  连通有向图 (边忽略方向后得到的无向图连通):
  存在欧拉回路 等价于所有点出入度相等.
  存在欧拉路径 等价于所有点出入度相等 或恰有一个点
  入度比出度多 1 和恰有 1 个点出度比入度多 1.
  如何求出一条欧拉路径?
  Prop.若G中有k个度为奇数的点,G可以划分为k/2条简单道路.

最短路

  最短路:有向边权图中两点间的所有路径中最短的一条.(若不连通,定义为 ∞)  

  负权边:负权图的最短路径中可能有无穷多条边.
  环路:不可能包含回路     ;
  松弛操作: d(x,v) = min(d(x,v),d(x,u)+w(u,v))
  三角不等式: d(x,v) ≤d(x,u)+w(u,v)
  最短路径树与最短路径图

 

Dijkstra  

  算法流程:维护已经找到最短路的点集S,不断地寻找距离S最近的点将其加入S.
  仅适用于边权非负的图.
  时间复杂度:O((V+E)log(V+E))/O((V+E)logV)/O(VlogV+E)

Bellman-Ford   

for(int i=n;i;--i)
	for(int j=m;j;--j)
		dis[v[j]]=min(dis[v[j]],dis[u[j]]+w[j]); 

  不断在最短路中加边.
  时间复杂度: O(VE)
  SPFA: deprecated
  Hack: 网格图, 次短路条数很多的图.

 Floyd  

for(int k=n; k; --k)
	for(int i=n; i; --i)
		for(int j=n; j; --j)
			dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);  

  可以看作 DP.
  同时求出每对点间的最短路.
  时间复杂度: O(V^3)

Johnson  

  每对顶点间的最短距离(稀疏有向图)
  重新赋予权重: w'(u,v)=w(u,v)+d(0,u)-d(0,v)
  一遍 Bellman-Ford+|V|遍Dijkstra.
  时间复杂度: O(V2logV + VE)

应用: 差分约束系统

    

  若 x = (x1,……,xn) 是差分约束系统的一个解,
  则 x + d 也是差分约束系统的一个解.
  约束图: 新建 0 点, w(0, i)(1 ≤i ≤n) 为 0; w(jl, il) 为 kl.

强连通分量  

  强连通分量 (SCC, Strongly Connected Component):
  有向图的极大点集 C ⊆ V, C 中任意两点可达.
  Kosaraju 算法: 以 vr 逆序对 GT = (V, ET) 作 DFS,则每棵DFS 树会是一个 SCC.
  Tarjan 算法: 使用一个栈来维护尚未被分到 SCC 中的节点,
  依据一个节点 u 是否能走到其 DFS 树中的祖先 (low(u)),来取出栈中节点分配成一个新的 SCC.
  Gabow 算法: 使用两个栈来取代 low, S 表示尚未被分配的节点, P 表示尚未被分配 SCC 的节点.
  时间复杂度均为 O(V + E)

  将 SCC 缩点, 原图变为 DAG.

边双连通分量  

  桥 (割边): 删掉之后会导致图不连通的边.
  边双连通分量 (BCC, biconnected component) 的等价定义:

    极大连通导出子图,不含桥.

    极大连通导出子图,使得任意两点间均有两条边不相交路径.

    极大连通导出子图,使得任意两条边都存在于一个简单回路中.

    极大连通导出子图,任意三点 a, b, c, 存在边不相交的路径 a → b, b →c.

    极大连通导出子图,存在一种给边赋向的方案使得其为SCC.

  判断割边: low(u) = ul

  将 BCC 缩点, 原图变为树.

点双连通分量  

  割点: 删掉之后会导致图不连通的点.
  点双连通分量 (块, block) 的等价定义:

 

       极大连通导出子图,删除其中任意一个点不会使其不连通.

    极大连通导出子图,任意两个点之间有两条点不相交路径.

    极大连通导出子图,任意三点 a, b, c 之间, 两条点不相交路径.

  注意到两个块可以有至多一个交点.

  割点的等价条件:

    根节点是割点当且仅当其DFS树至少有两个子树.

    非根节点是割点当且仅当 low(u) < ul.

  将 Block 缩成环, 原图变为仙人掌.

最小生成树基本概念:环与割  

  切割: (S, V -- S), 对 V 的一个划分.
   横跨: (u, v) 一个端点在 S, 另一个端点在 V -- S, 则称 (u, v)横跨割 (S, V -- S).
   尊重: 边集 A 中不存在横跨割 (S, V - S) 的边, 则称(S, V -- S) 尊重 A.
   轻量级边: (u, v) 是横跨 (S, V -- S) 的所有边中权重最小的.

MST 基本定理   

  对于任意 A ⊆ E, 存在 G 的最小生成树 T, A ⊆ T, 任意e = (u, v) ∈ A,
   存在尊重 A 的切割 (S, V -- S), (u, v) 是(S, V -- S) 的一条轻量级边,
   令 B = A ∪ (u, v), 则存在最小生成树 T', B ⊆ T'.(这条边被称作安全边)

MST 的求解算法    

    Prim 算法: 类似于 BFS/Dijkstra, 维护一个连通的 A, 不断寻找 (A, S -- A) 中的轻量级边.
  时间复杂度: O((V + E)logV) / O(VlogV + E)
  Kruskal 算法: 从小到大枚举每条边 (u, v), 若 u 和 v 在 A 中尚不连通, 就将 (u, v) 加入 A.
  时间复杂度: O(V + E log E)
  Boruvka 算法: 找到每个点的最小邻边, 形成了环套树森林,将每一个环套树缩点.
  时间复杂度: O((V + E)logV)

MST 的性质  

    若图 G 的一条边 (u, v) 在某棵最小生成树 T 中,则该条边是某个切割 (S, V -- S) 的轻量级边.
  任意两棵最小生成树的有序边权序列相同.
  对于非负边权图, MST 等价于权值和最小的边集, 使得图连通.
  MST 是瓶颈生成树 (最大边权最小的生成树).

 最近公共祖先:LCA问题  

  LCA(Lowest Common Ancestor, 最近公共祖先):
  LCA(u,v) 定义为 u 和 v 的所有公共祖先中深度最大的那一个.

  倍增: fa[u][j] 表示从 u 向上跳 2^j 步可以到达的点.
  时间复杂度: O(n log n) -- O(log n)

  树链剖分: 重儿子是所有儿子中 size 最大的那个.
  时间复杂度: O(n) -- O(log n)

  DFS 序 +ST: 利用 DFS 序的性质将问题转化为求区间最小值, 再使用 Sparse Table 求解.
  时间复杂度: O(n log n) -- O(1)

  Tarjan: 离线查询, 使用并查集维护当前栈中节点已经遍历过的子树.
  时间复杂度: O(n + m)

 

与 RMQ 问题的联系  

  RMQ(Range Minimum/Maximum Query): 查询区间最值.

  静态: ST(Sparse Table)(O(n log n) -- O(1)).
  动态: 线段树 (O(n) -- O(log n)).

  从 LCA 到 RMQ: 利用 DFS 序.(特殊的 ±1 RMQ)
  从 RMQ 到 LCA: 利用笛卡尔树.

  在线查询的线性做法: 以 log n/2 为大小分块, 再使用 Sparse Table.
  时间复杂度: O(n) -- O(1)

 

posted @ 2018-10-03 22:03  孟东行#  阅读(497)  评论(2编辑  收藏  举报