六、图与网络分析
基本概念、术语
某个边的两个端点相同,称为环;两点之间有多于一条的边,成为多重边。一个无环、无多重边的图称为简单图,无环但允许有多重边的图称为多重图。
次:以 viv_ivi 为端点的边的数目称为点 viv_ivi 在 GGG 中的次,记为 d(vi)d(v_i)d(vi) 。如果有环,按两条边记。
所有点的次之和为边的两倍,即 ∑d(vi)=2m\sum d(v_i)=2m∑d(vi)=2m ,mmm 为图的边数。
奇数次顶点的个数总为偶数。简单证明为:将所有点分为奇数次点和偶数次点,两者的次相加为 2m2m2m ,则奇数次点的个数为 2m2m2m (偶数)减去偶数次点的次(偶数),结果仍为偶数。
次为 1 的点称为悬点,其所关联的边为悬边。次为 0 的点位孤立点。
链中所有顶点都不同,称为初等链;所有边都不同,称为简单链;链的长度为所包含的边数。
平凡图:点数 n=1n=1n=1 ,边数 m=0m=0m=0 的图。零图:边数 m=0m=0m=0 的图。
连通图:每对节点都有一条链(路)连通。树:无圈连通图。
正则图:每个点的次数均相同。
子图是对点或边做删除运算,支撑子图点必须一样,边可以有所减少。
邻接矩阵,相邻两个顶点连通则对应元素为 1 ,反之为 0 ,对角线元素为 0 。
可达矩阵,只要能连通,那对应元素就为 1 ,反之为 0 ,对角线元素为 1 。
关联矩阵,横的是点,纵的是边,点如果是边的起点,对应元素为 1 ;是边的终点,则对应元素为 -1 ;若无关,则对应元素为 0 。
权矩阵,相邻两点可连通就是权,不可到就是无穷,对角线元素为 0 。
最小支撑树问题
无圈的连通图称为树。
若 TTT 为树,且树中点的个数 nT≥2n_T\geq2nT≥2 ,则 TTT 中至少有两个悬挂点。
这里说要树的点数大于等于 2 ,因为有可能一个树只有一个孤立点,那就没有两个悬挂点了。
图 TTT 是树,则 TTT 中的边数 mmm 等于点数 nnn 减一,即 m=n−1m=n-1m=n−1 。
注意这是一个必要条件,即如果 m=n−1m=n-1m=n−1 ,图 GGG 不一定是树,比如一个三角形加上一个孤立点。
图 TTT 是树的充分必要条件是任意两个顶点之间恰有一条链。
设图 TTT 是图 GGG 的支撑子图,如果 TTT 是一个树,称其为 GGG 的一个支撑树。
图 GGG 有支撑树的充分必要条件是图 GGG 是连通的。
最小支撑树有两个定理:
- 对任意一个树外的边,唯一决定一个圈,如果树是最小支撑树,则该边是那个圈里面的最大边。
- 对于任意一个树上的边,唯一决定一个割集,如果树是最小支撑树,则该边是割集里最小边。
避圈法,将网络中的边按权大小排序,从最小边选起,保证不构成圈,直到边数等于点数减一为止。
破圈法,每次找一个圈,去掉里面最大的边,直到没有圈。
最短路问题
最短路问题的网络模型如下:minz=∑(i,j)∈Ecijxijs.t.{x12+x13+x15=1x13+x23−x34−x35=0x57+x67=1⋯xij=0或1,(i,j)∈E\min z=\sum_{(i,j)\in E} c_{ij}x_{ij} \\ s.t.\begin{cases} x_{12}+x_{13}+x_{15}=1\\ x_{13}+x_{23}-x_{34}-x_{35}=0\\ x_{57}+x_{67}=1 \\ \cdots \\ x_{ij}=0或1,(i,j)\in E\end{cases}minz=(i,j)∈E∑cijxijs.t.⎩⎨⎧x12+x13+x15=1x13+x23−x34−x35=0x57+x67=1⋯xij=0或1,(i,j)∈E 其中,cijc_{ij}cij 表示点 vi→vjv_i\to v_jvi→vj 的距离,xijx_{ij}xij 表示最短路是否经过弧 (i,j)(i,j)(i,j) 。决策变量个数为边的个数,约束条件个数为点的个数。约束条件中,第一个约束表示起点连接的边只能有一个在最短路中;中间的约束为中间点的约束,连进中间点的等于从中间点连出的;最后一个约束表示连进终点的边只能有一个在最短路中。
Dijkstra 算法
步骤:
- 初始化,令 k=0k=0k=0 ,S(0)={vs}S^{(0)}=\{v_s\}S(0)={vs} ,表示永久标号点的集合,T(0)={v1,v2,⋯ ,vn}T^{(0)}=\{v_1,v_2,\cdots,v_n\}T(0)={v1,v2,⋯,vn} 表示临时标号点的集合,d(0)(vs)=0,d(0)(vi)=∞(vi∈T(0))d^{(0)}(v_s)=0,d^{(0)}(v_i)=\infty(v_i\in T^{(0)})d(0)(vs)=0,d(0)(vi)=∞(vi∈T(0)) 。λ(0)(vi)=vs,resent=vs\lambda^{(0)}(v_i)=v_s,resent=v_sλ(0)(vi)=vs,resent=vs ,表示最新获得永久标号的顶点。
- k=k+1k=k+1k=k+1 ,对 vl∈T(k−1)v_l\in T^{(k-1)}vl∈T(k−1) ,计算:d(k)(vl)=min{d(k−1)(vl),d(resent)+w(resent,vl)}d^{(k)}(v_l)=\min\{d^{(k-1)}(v_l),d(resent)+w(resent,v_l)\}d(k)(vl)=min{d(k−1)(vl),d(resent)+w(resent,vl)} 如果 d(k)(vl)<d(k−1)(vl),λ(k)(vl)=resentd^{(k)}(v_l)<d^{(k-1)}(v_l),\lambda^{(k)}(v_l)=resentd(k)(vl)<d(k−1)(vl),λ(k)(vl)=resent ;否则,λ(k)(vl)=λ(k−1)(vl)\lambda^{(k)}(v_l)=\lambda^{(k-1)}(v_l)λ(k)(vl)=λ(k−1)(vl) 。
- 若 vkv_kvk 满足 vk=min{d(k)(vl)}v_k=\min\{d^{(k)}(v_l)\}vk=min{d(k)(vl)} ,则 resent=vkresent=v_kresent=vk ,S(k)=S(k−1)⋃{vk},T(k)=T(k−1)−{vk}S^{(k)}=S^{(k-1)}\bigcup\{v_k\},T^{(k)}=T^{(k-1)}-\{v_k\}S(k)=S(k−1)⋃{vk},T(k)=T(k−1)−{vk} 。若 k=nk=nk=n ,则算法结束,否则转第二步。
实际写题这样写麻烦且不直观,可以用表格写法。
大概是这样,这样每次都可以对照 S+wijS+w_{ij}S+wij 和 TTT 。从 TTT 中选择最小的标记一下,等所有标记完,就可以依次回溯到最短路。
Bellman-Ford 算法
Dijkstra 的一个缺点就是有负权就不能用,Bellmam-Ford 适用于有负权但不含负回路的有向赋权图。根据下标依次计算,如 d12=min{d11+w12,d12+w22+⋯ }d_{12}=\min\{d_{11}+w_{12},d_{12}+w_{22}+\cdots\}d12=min{d11+w12,d12+w22+⋯}
如果第 3 项最小,说明 v3v_3v3 是 v1→v2v_1\to v_2v1→v2 最短路的终点 v2v_2v2 的前一个顶点号。
Floyd 算法
前面两个算法是计算单源到多个顶点的最短路问题,而 Floyd 算法可以求任意两个顶点之间的最短路,且适用于有负权网络。它的思想是比较直接到达和经过一个中间点到达,哪个距离更短。每次记录的路径标号表示最短路的第一条弧的终点。