网络与流--图论笔记

news/2024/7/15 18:23:54 标签: 图论, 网络, 数据结构

网络与流 – 潘登同学的图论笔记

文章目录

  • 网络与流 -- 潘登同学的图论笔记
        • (算法)Ford-Fulkerson(G) (最大流最小割算法)

假设 G = ( V , E ) G=(V,E) G=VE是一个连通无重边且不包含自环的有向图,如果G中

(1)只有一个入度为0的顶点,记之作为s,称为源

(2)只有一个出度为0的顶点,记之作为t,称为汇

(3)每条有向边 e = ( u , v ) e=(u,v) e=uv都存在一个非负权值 C u v C_{uv} Cuv,称作边的容量

则称G是一个网络或流网络,也记作 G = ( V , E , s , t , C ) G=(V,E,s,t,C) G=VEstC

注:(问题转化)

①若存在重边,对重边求和作为一条边总容量

②自环的存在与否不影响问题的分析

③在实际应用中,还要考虑中转站的容量上限

④如果有多个源或者汇,那么新增两个顶点,S,T作为 源的源 和 汇的汇 ,并且将足够大的容量 C 0 C_0 C0赋予这些新加的有向边(指的是S到多个源s的边,T到多个汇t的边)

  • 前驱、后继

假设顶点 v ∈ V v∈V vV,定义v的前驱为 p r e d ( v ) = { u ∣ ( u , v ) ∈ E } pred(v) = \{u|(u,v)∈E\} predv)={uuvE}

定义v的后继为 s e c c ( v ) = { u ∣ ( u , v ) ∈ E } secc(v)=\{u|(u,v)∈E\} secc(v)={uuvE}

  • 流(流量)

若实值函数 f : E → R f: E→\mathcal{R} f:ER满足

(1)容量限制:对所有 e = ( u , v ) ∈ E e=(u,v)∈E e=uvE,有 f u v = f ( e ) ≤ C u v f_{uv} = f(e) ≤C_{uv} fuv=feCuv

(2)流量守恒:对所有顶点 v ∈ V − { s , t } , ∑ ( u ∈ p r e d ( v ) ) f u v = ∑ ( u ∈ s u c c ( v ) ) f u v v∈V-\{s,t\}, ∑_{(u∈pred(v))}f_{uv} = ∑_{(u∈succ(v))}f_{uv} vV{s,t},(upred(v))fuv=usucc(v))fuv

则称他是网络的一个容许流分布,或简称一个流, f u v f_uv fuv称作边(u,v)上的流量,若 e = ( u , v ) e=(u,v) e=uv满足 f u v = C u v f_{uv} = C_{uv} fuv=Cuv,则称e为饱和边

  • 流入(出)量

对所有顶点 v ∈ V − { s } v∈V-\{s\} vV{s},定义 f ( ∗ , v ) = ∑ ( u ∈ p r e d ( v ) ) f u v f( * ,v) = ∑_{(u∈pred(v))}f_{uv} f,v=(upred(v))fuv,称作顶点v的总流入量

对所有顶点 u ∈ V − { t } u∈V-\{t\} uV{t},定义 f ( u , ∗ ) = ∑ ( v ∈ p r e d ( u ) ) f u v f(u ,*) = ∑_{(v∈pred(u))}f_{uv} fu,=vpred(u))fuv,称作顶点v的总流出量

(为完整性考虑,补充定义 f ( ∗ , s ) = 0 , f ( t , ∗ ) = 0 f(* ,s)= 0, f(t,*)= 0 f,s=0ft,=0

流量守恒表明:流在除了元和汇以外的各个顶点的总流入等于总流出量

  • 最大流

设f是网络图G的一个流, f ( s , ∗ ) f(s,*) fs,称作流f的流量,即源s的总流出量,记作|f|。若G的任意一个流f’都满足 ∣ f ∣ ≥ ∣ f ’ ∣ |f|≥|f’| ff,则称f是G的一个最大流

  • S-T割(非常重要)

网络 G = ( V , E , s , t , c ) G = (V,E,s,t,c) G=V,E,s,t,c)中,任何一个满足 s ∈ S , t ∈ T = V − S s∈S,t∈T = V-S sStT=VS的顶点V的划分(S,T)称作一个S-T割,简称割

一个S-T割的容量定义为 ∑ ( u ∈ S , v ∈ T , u v ∈ E ) C u v ∑_{(u∈S,v∈T,uv∈E)}C_{uv} uSvTuvECuv,记作 C u v ( S , T ) C_{uv}(S,T) CuvST

如果图G的S-T割使得任意一个G的S-T割(S’,T’)都有 C a p ( S , T ) ≤ C a p ( S ′ , T ′ ) Cap(S,T)≤Cap(S',T') CapSTCapST则称(S,T)是图G的一个最小S-T割,简称最小割

  • 符号说明

网络 G = ( V , E , s , t , c ) G = (V,E,s,t,c) G=V,E,s,t,c中,假设A、B都是V的非空子集,定义 f ( A , B ) = ∑ ( u ∈ S , v ∈ T , u v ∈ E ) f u v f(A,B) = ∑_{(u∈S,v∈T,uv∈E)}f_{uv} fAB=uSvTuvEfuv,即从A穿出进入B的边的总流量,定义 C ( A , B ) = ∑ ( u ∈ A , v ∈ B , u v ∈ E ) C u v C(A,B)= ∑_{(u∈A,v∈B,uv∈E)}C_{uv} CAB=uAvBuvECuv,即从A穿出进入B的边的总容量

  • (定理)割流量 = 总流量

假设 G = ( V , E , s , t , c ) G =(V,E,s,t,c) G=V,E,s,t,c是一个网络,令f是一个流,(S,T)是一个S-T割,则通过该割的流量 = 由源s发出的流量;即 f ( S , T ) − f ( T , S ) = ∣ f ∣ f(S,T)-f(T,S) = |f| fST)fT,S=f

特别地,有 f ( ∗ , t ) = ∣ f ∣ f(*, t) = |f| ft)=f

证明:(对|S|进行归纳证明)

①当 S = s S={s} S=s的时候显然成立

②假设定理对于 ∣ S ∣ < k |S|<k Sk的时候成立;

∣ S ∣ = k |S| = k S=k时, 任取 v ∈ S − { s } v∈S-\{s\} vS{s},令 S ’ = S − { v } S’= S-\{v\} S=S{v} T ′ = T ∪ { v } T' = T∪\{v\} T=T{v}(这一步是把S中k个对象将为k-1个,然后可以用假设)

则由 ∣ S ′ ∣ = k − 1 |S'| = k-1 S=k1 可知 f ( S ’ , T ′ ) − f ( T ’ , S ’ ) = ∣ f ∣ f(S’,T')-f(T’,S’)=|f| fSTfTS=f

将顶点v添加到S’后,有
$$

\begin{aligned}
f(S,T)-F(S,T) &= (f(S’,T’)-f(S’,{v})+f({v},T))-(f(T’,S’)-f(T,{v})+f(S’,{v}))\
&= f(S’,T’) - f(T’,S’) + (f({v},T) + f({v},S’)- f(T,{v}) - f(T,{v}))\
&= f(S’,T’) - f(T’,S’) + f(v,)- f(,v)\
&= f(S’,T’) - f(T’,S’) = |f|\
\end{aligned}
$$

  • 最大流的上限由最小割决定

设G是一个网络,令f是G的一个流,(S,T)是G的一个S-T割,则 ∣ f ∣ ≤ C a p ( S , T ) |f|≤Cap(S,T) fCapST

证明: 由上面的定理
∣ f ∣ = f ( S , T ) − f ( T , S ) ≤ f ( S , T ) = ∑ ( u ∈ S , v ∈ T , u v ∈ E ) f u v ≤ ∑ ( u ∈ S , v ∈ T , u v ∈ E ) C u v = C a p ( S , T ) \begin{aligned} |f| &= f(S,T) - f(T,S)≤f(S,T)\\ &= ∑_{(u∈S,v∈T,uv∈E)}f_{uv} \\ &≤ ∑_{(u∈S,v∈T,uv∈E)}C_{uv} = Cap(S,T)\\ \end{aligned} f=fSTfTSfST=uSvTuvEfuvuSvTuvECuv=CapST

  • 推论

设G是一个网络,f是一个流,(S,T)是一个S-T割,则若 ∣ f ∣ = C a p ( S , T ) |f| = Cap(S,T) f=Cap(S,T),则f是一个最大流且(S,T)是一个最小割

证明:假设 f 1 f_1 f1是任意一个流,则由上面定理可得, ∣ f 1 ∣ ≤ ∣ f ∣ |f_1|≤|f| f1f,则 f f f是一个最大流

假设 ( S 1 , T 1 ) (S_1,T_1) S1,T1)是任意一个S-T割,则由上面定理,可得 C a p ( S , T ) = ∣ f ∣ ≤ C a p ( S 1 , T 1 ) Cap(S,T)=|f| ≤Cap(S_1,T_1) CapST=fCapS1,T1,因此(S,T)是一个最小S-T割

(因此只要找到最小S-T割就能找到最大流)

  • 剩余图

假设网络 G = ( V , E , s , t , c ) G =(V,E,s,t,c) G=V,E,s,t,c中有流f,则可如下定义G关于f的剩余图为 G f = ( V , E , s , t , c ’ ) G_f =(V,E,s,t,c’) Gf=V,E,s,t,c

(1) G f G_f Gf的顶点集与G的顶点相同

(2) G f G_f Gf的边集合 E f E_f Ef有两类: { ( u , v ) ∣ f u v < C u v } \{(u,v)|f_{uv}<C_{uv}\} {uvfuv<Cuv},称为前向边; { ( u , v ) ∣ f v u > 0 } \{(u,v)|f_{vu}>0\} {uvfvu>0},称为后向边;
E f = { ( u , v ) ∣ f u v < C u v 或 f u v > 0 } E_f = \{(u,v)|f_{uv}<C_{uv} 或 f_{uv}>0\} Ef={uvfuv<Cuvfuv>0}

(3)容量 C u v ’ = C u v − f u v ( 当 f u v < C u v ) o r f v u ( 当 f v u > 0 ) C_{uv}’= C_{uv} - f_{uv} (当f_{uv}<C_{uv}) or f_{vu} (当f_{vu} > 0) Cuv=Cuvfuv(fuv<Cuv)orfvu(fvu>0)

(说白了就是把边反过来)

如图一个由流构造剩余图的例子:

一个剩余图

  • 可增广道路

假设网络 G = ( V , E , s , t , c ) G =(V,E,s,t,c) G=V,E,s,t,c中由 流 f , G 关 于 f 的 剩 余 图 流f,G关于f的剩余图 fGf中的简单s-t道路(就是由源s到汇t的道路)P称作可增广道路,定义bottleneck(P,f)为P所经过各边的最小流量(bottleneck意思是瓶颈)

可以由增广道路P构造G的一个新流f’;

当(u,v)是P中的前向边, f u v ’ = f u v + b o t t l e n e c k ( P , f ) f_{uv}’ = f_{uv} + bottleneck(P,f) fuv=fuv+bottleneckPf

当(u,v)是P中的后向边, f u v ’ = f u v − b o t t l e n e c k ( P , f ) f_{uv}’ = f_{uv} - bottleneck(P,f) fuv=fuvbottleneckPf

其他情况时, f u v fuv fuv

可以验证函数f’满足容量条件和守恒条件:

(1)满足容量条件。对于不在道路P上的边而言,不产生任何变化

如果(u,v)是P中的前向边, f u v ’ = f u v + b o t t l e n e c k ( P , f ) ≤ f u v + C u v ’ = f u v + C u v − f u v = C u v f_{uv}’ = f_{uv} + bottleneck(P,f)≤f_{uv} + C_{uv}’ = f_{uv} + C_{uv} - f_{uv} = C_{uv} fuv=fuv+bottleneckPffuv+Cuv=fuv+Cuvfuv=Cuv

如果(u,v)是P中的后向边, f u v ’ = f u v − b o t t l e n e c k ( P , f ) ≤ f u v ≤ C u v f_{uv}’ = f_{uv} - bottleneck(P,f)≤f_{uv}≤ C_{uv} fuv=fuvbottleneckPffuvCuv

(2)满足守恒条件,对于不在道路P上的顶点而言,不产生任何变化

对于在P上的顶点 v ∈ V − { s , t } v∈V-\{s,t\} vV{s,t},假设在道路P上与v关联的边是(u,v)和(v,w)

①如果(u,v)和(v,w)都是P中的前向边,则

f ’ ( ∗ , v ) − f ’ ( v , ∗ ) = ( f ( ∗ , v ) + b o t t l e n e c k ( P , f ) ) − ( f ( v , ∗ ) + b o t t l e n e c k ( P , f ) ) = f ( ∗ , v ) − f ( v , ∗ ) = 0 \begin{aligned} f’(*,v) - f’(v,*) &= (f(*,v) + bottleneck(P,f)) - (f(v,*) + bottleneck(P,f))\\ &=f(*,v) - f(v,*) = 0 \end{aligned} fvfv,=(f(v)+bottleneckPf)(fv+bottleneckPf)=f(v)fv=0

②如果(u,v)和(v,w)都是P中的后向边,则
f ’ ( ∗ , v ) − f ’ ( v , ∗ ) = ( f ( ∗ , v ) − b o t t l e n e c k ( P , f ) ) − ( f ( v , ∗ ) − b o t t l e n e c k ( P , f ) ) = f ( ∗ , v ) − f ( v , ∗ ) = 0 \begin{aligned} f’(*,v) - f’(v,*) &= (f(*,v) - bottleneck(P,f)) - (f(v,*) - bottleneck(P,f))\\ &=f(*,v) - f(v,*) = 0\\ \end{aligned} fvfv,=(f(v)bottleneckPf)fvbottleneckPf=f(v)fv=0
③如果(u,v)是P中的前向边, (v,w)是P中的后向边,则
f ’ ( ∗ , v ) − f ’ ( v , ∗ ) = ( f ( ∗ , v ) − b o t t l e n e c k ( P , f ) + b o t t l e n e c k ( P , f ) ) − f ( v , ∗ ) = f ( ∗ , v ) − f ( v , ∗ ) = 0 \begin{aligned} f’(*,v) - f’(v,*) &= (f(*,v) - bottleneck(P,f)+ bottleneck(P,f)) - f(v,*)\\ =f(*,v) - f(v,*) = 0\\ \end{aligned} fvfv,=f(v)fv=0=(f(v)bottleneckPf+bottleneckPf)fv
④如果(u,v)是P中的后向边, (v,w)是P中的前向边,则
f ’ ( ∗ , v ) − f ’ ( v , ∗ ) = f ( ∗ , v ) − ( f ( v , ∗ ) − b o t t l e n e c k ( P , f ) + b o t t l e n e c k ( P , f ) ) = f ( ∗ , v ) − f ( v , ∗ ) = 0 \begin{aligned} f’(*,v) - f’(v,*) &= f(*,v) - (f(v,*)- bottleneck(P,f)+ bottleneck(P,f))\\ &=f(*,v) - f(v,*) = 0\\ \end{aligned} fvfv,=f(v)fvbottleneckPf+bottleneckPf)=f(v)fv=0

而且 ∣ f ’ ∣ = f ’ ( s , ∗ ) = f ( s , ∗ ) + b o t t l e n e c k ( P , f ) > f ( s , ∗ ) = ∣ f ∣ |f’| = f’(s,*) = f(s,*) + bottleneck(P,f) > f(s,*) = |f| f=fs,=fs,+bottleneckPf>fs,=f,即流的流量得以提升

  • 最大流,最小割定理

假设f是网络 G = ( V , E , s , t , c ) G =(V,E,s,t,c) G=V,E,s,t,c的一个流,则一下陈述等价

(a)f是一个最大流

(b)当且f的剩余图中不存在增广道路

©存在G的一个S-t割(S,T)使得 ∣ f ∣ = C a p ( S , T ) |f| = Cap(S,T) f=CapST

证明:(a)–>(b)

如果当前关于f的剩余图中存在可增广道路,则可以通过这条道路扩大流,与f是最大流矛盾

(b)–>©

假设f是不存在可增广道路的流,设S是在当前剩余图由s可达的顶点之集合,显然s∈S,且t∉S,否则存在增广道路;

T = V − S , 假 设 u ∈ S , v ∈ T , 若 ( u , v ) ∈ E , 则 必 有 f u v = C u v , 否 则 ( u , v ) ∈ E f T = V - S,假设u∈S,v∈T,若(u,v)∈E,则必有f_{uv} = C_{uv},否则(u,v)∈E_f T=VSuSvTuvEfuv=CuvuvEf,v也由s可达,与S的定义矛盾;

( v , u ) ∈ E , 则 必 然 有 f v u = 0 , 否 则 ( u , v ) ∈ E f (v,u)∈E,则必然有f_{vu}=0,否则(u,v)∈E_f v,uEfvu=0uvEf,v也由s可达,与S的定义矛盾

©–>(a) :由上面的推论可得

(算法)Ford-Fulkerson(G) (最大流最小割算法)

输入: 网络G =(V,E,s,t,c)

输出: G的一个最大流f

①初始流量选为0流量,即对所有边 u v , f u v ← 0 uv,f_{uv}←0 uvfuv0

②构造G关于f的剩余图 G f G_f Gf

③若 G f G_f Gf中存在增广道路P,则按照前述方法构造由增广道路P,构造G的一个新流 f ′ f' f, f ← f ’ f←f’ ff,转到②

否则输出f

(通俗来说,就是在图中随便找一条路,再把路反过来,再找一条路,再把路反过来,往复这个过程直达找不到路就是最大流了)

实现算法过程注意几点:(是我在写代码中的出来的关键)

因为f永远是正的,跟初始边的方向是同向的,所以f不需要再建一个图,直接把f并入边的属性即可cost=[f,c],所以图的数据结构要修改

增广道路的搜寻方法多种多样,可以用前面的DFS(固定起始点s,当path的最后一个元素是t时输出path即可),我这里采用BFS(广度优先搜素,详细的可以继续往后看,会说BFS)

剩余图也用同一个数据结构,但是边属性只有一个C,cost=c

接下来,还是看代码!!!冲!!!

#%%Ford-Fulkerson算法求解最大流问题
from Vertex import Vertex # 导入Vertex
from Graph import Graph  # 导入之前实现的Graph
import sys

class New_Vertex(Vertex):  # 某一个具体问题的数据结构需要继承原有数据结构
    def __init__(self, key):
        super().__init__(key)
        self.color = 'white'  # 新增类属性(用于记录节点是否被走过)
        self.dist = sys.maxsize  # 新增类属性(用于记录strat到这个顶点的距离)初始化为无穷大
        self.pred = None  # 顶点的前驱 BFS需要

    # 新增类方法, 设置节点颜色
    def setColor(self, color):
        self.color = color

    # 新增类方法, 查看节点颜色
    def getColor(self):
        return self.color

    # 新增类方法, 设置节点前驱
    def setPred(self, p):
        self.pred = p

    # 新增类方法, 查看节点前驱
    def getPred(self):  # 这个前驱节点主要用于追溯,是记录离起始节点最短路径上
        return self.pred    # 该节点的前一个节点是谁

    # 新增类方法, 设置节点距离
    def setDistance(self, d):
        self.dist = d

    # 新增类方法, 查看节点距离
    def getDistance(self):
        return self.dist

class New_Graph(Graph):  # 继承Graph对象
    def __init__(self):
        super().__init__()

    # 重载方法  因为原先Graph中新增节点用的是Vertex节点,但现在是用New_Vertex
    def addVertex(self, key):   # 增加节点
        '''
        input: Vertex key (str)
        return: Vertex object
        '''
        if key in self.vertList:
            return
        self.numVertices = self.numVertices + 1
        newVertex = New_Vertex(key)   # 创建新节点
        self.vertList[key] = newVertex
        return newVertex

# %广度优先算法(先从距离为1开始搜索节点,搜索完所有距离为k才搜索距离为k+1)
'''
为了跟踪顶点的加入过程,并避免重复顶点,要为顶点增加三个属性
    距离distance:从起始顶点到此顶点路径长度
    前驱顶点predecessor:可反向追随到起点
    颜色color:标识了此顶点是尚未发现(白色),已经发现(灰色),还是已经完成探索(黑色)
还需用一个队列Queue来对已发现的顶点进行排列
    决定下一个要探索的顶点(队首顶点)

BFS算法过程
    从起始顶点s开始,作为刚发现的顶点,标注为灰色,距离为0,前驱为None,
    加入队列,接下来是个循环迭代过程:
        从队首取出一个顶点作为当前顶点;遍历当前顶点的邻接顶点,如果是尚未发现的白
        色顶点,则将其颜色改为灰色(已发现),距离增加1,前驱顶点为当前顶点,加入到队列中
    遍历完成后,将当前顶点设置为黑色(已探索过),循环回到步骤1的队首取当前顶点
'''

# 队列 -- 也是BFS需要的
class Queue:
    def __init__(self):
        self.items = []

    def isEmpty(self):
        return self.items == []

    def enqueue(self, items):   # 往队列加入数据
        self.items.insert(0, items)

    def dequeue(self):
        return self.items.pop()

    def size(self):
        return len(self.items)

class max_flow_graph():
    # 创建图和初始化流f
    # 注意图中的cost由[flow, cost] 组成 前者表示流量 后者表示容量
    # 既可以传入字典也能传入邻接矩阵
    def createGraph_f(self, g_dict=None, g_matrix=None):
        '''
        input: g_dict(邻接表) or g_matrix(邻接矩阵)
        output: directgraph(有向图)
        '''
        graph = New_Graph()
        # f = Graph()
        if g_dict:
            for from_key in g_dict:
                for to_key in g_dict[from_key]:
                    # 这里就是注意提到的f跟着c走所以原图的c是[f,c],这个很重要
                    graph.addEdge(from_key, to_key[0], [0, to_key[1]])

        elif g_matrix:
            # 先给顶点起个名字
            name = [str(i) for i in range(1, len(g_matrix)+1)]
            for i, from_key in enumerate(name):
                for j, to_key in enumerate(name):
                    if g_matrix[i][j] != float('inf'):
                        graph.addEdge(from_key, to_key, [0,g_matrix[i][j]])
        return graph

    # 得到简单的s-t道路P
    def BFS(self, gf, start, end):   # g是图,start是起始的节点
        '''
        g: Gf 剩余图
        start: key
        end: key
        return: p 以节点为元素的道路列表
        '''
        start.setDistance(0)  # 距离
        start.setPred(None)  # 前驱节点
        vertQueue = Queue()   # 队列
        vertQueue.enqueue(start)  # 把起始节点加入图中
        while vertQueue.size() > 0:   # 当搜索完所有节点时,队列会变成空的
            currentVert = vertQueue.dequeue()  # 取队首作为当前顶点
            for nbr in currentVert.getConnections():  # 遍历临接顶点
                if (nbr.getColor() == 'white'):   # 当邻接顶点是灰色的时候
                    nbr.setColor('gray')
                    nbr.setDistance(currentVert.getDistance() + 1)
                    nbr.setPred(currentVert)
                    vertQueue.enqueue(nbr)
            currentVert.setColor('balck')
        P = []
        P.append(end)
        while end.getPred():
            end = end.getPred()
            P.append(end)
        return P[::-1]

    # 由原图构造剩余图
    def create_remain_graph(self, g):
        '''
        input: g有向图(包含flow流量和cost容量)
        output: gf剩余图(就是把flow的方法反过来的一个图)
        '''
        # 剩余图gf的边只有一个属性就是cost
        gf = New_Graph()
        # 获得顶点集
        verlist = g.vertList
        for i in verlist:
            from_key = verlist[i]
            gf.addVertex(from_key.getId())
            for to_key in from_key.getConnections():
                gf.addVertex(to_key.getId())
                f = from_key.getWeight(to_key)[0]  # f表示流
                c = from_key.getWeight(to_key)[1]  # c表示容量
                # 前向边
                if f <  c:
                    c_new = c - f
                    gf.addEdge(from_key.getId(), to_key.getId(), c_new)
                # 后向边
                if f > 0:
                    gf.addEdge(to_key.getId(), from_key.getId(), f)
        return gf

    def print_flow(self, g):
        '''
        input: g 带流的图
        output: flow_matrix
        '''
        result = [[0]*len(g) for _ in range(len(g))]
        for i in range(1, len(g)):
            from_key = str(i)
            for j in range(i+1, len(g)+1):
                to_key = str(j)
                from_Vertex = g.getVertex(from_key)
                to_Vertex = g.getVertex(to_key)
                try:
                    result[i-1][j-1] = from_Vertex.getWeight(to_Vertex)[0]
                except:
                    pass
        return result
                
    def Ford_Fulkerson(self, g):
        '''
        算法主流程:
            1.初始化流为0 (f<--0)(我把这步放到createGraph中了)
            2.构造G关于f的剩余图gf
            3.若gf中存在增广道路P, 则由增广道路P构造G的一个新流f’
              f <-- f' 转步骤2
              否则输出f

        Parameters
        ----------
        g : graph
            由createGraph_f创建的图.
            
        Returns
        -------
        choice matrix
            流量方案的选择(每一行表示一个流出点, 每一列表示一个流入点).

        '''
        # 构造g关于f的剩余图Gf
        gf = self.create_remain_graph(g)
        # 求出Gf的增广道路
        P = self.BFS(gf, gf.getVertex('1'), gf.getVertex(str(len(g))))
        # 若存在增广道路 则由增广道路构造G的一个新流f
        while len(P)>1:
            # 计算增广路中的bottleneck
            bottleneck = 1e5
            for i in range(len(P)-1):
                if P[i].getWeight(P[i+1]) < bottleneck:
                    bottleneck = P[i].getWeight(P[i+1])
            # 更新f
            for i in range(len(P)-1):
                u_name = P[i].getId()
                v_name = P[i+1].getId()
                u = g.vertList[u_name]
                v = g.vertList[v_name]
                c = u.getWeight(v)
                # 如果(u,v)是g中的正向边
                if v in u.getConnections():
                    c[0] += bottleneck
                elif u in v.getConnections():
                    c[0] -= bottleneck
                # 流一定是正向的,只有剩余图的边有可能是反向的
                g.addEdge(u_name, v_name, c)
            # 更新gf
            gf = self.create_remain_graph(g)
            # 求出Gf的增广道路
            P = self.BFS(gf, gf.getVertex('1'), gf.getVertex(str(len(g))))
        return self.print_flow(g)

if __name__ == '__main__':
    g_dict ={'1':[['2', 10], ['3', 10]],
             '2':[['3', 2], ['4', 4], ['5', 8]],
             '3':[['5', 9]],
             '4':[['6', 10]],
             '5':[['4', 6], ['6', 10]]}
    s = max_flow_graph()
    g = s.createGraph_f(g_dict)
    print(s.Ford_Fulkerson(g))

(注:如果各边的容量都是整数,则每次f←f‘的更新都使得流的值至少增加1,因此算法至多再 ∑ ( v ∈ s u s s ( s ) C s v ∑_{(v∈suss(s)}C_{sv} vsuss(s)Csv次结束,如果各边的容量是一般实数,那么该算法永远运行下去)

可以将二部图的匹配问题转化为网络流图

步骤:(1)将原图所有无向边改为有向边,由X中顶点指向Y中顶点

(2)添加一个超源顶点S和一个超汇顶点T

(3)添加S到X中每个顶点的有向边,添加Y中每个顶点到T的有向边

(4)所有有向边的容量都设置为1

所得的图称作匹配网络,显然二部图中的最大匹配对应匹配网络的最大流。(看到这里是不是豁然开朗了,原来网上说的二部图的最大匹配算法的原理是这样)

我的离散数学的图论笔记就到此为止了!!,如果你也是一个证明一个证明看下来,一行代码一行代码地敲下来的话,你已经很强啦!!!


http://www.niftyadmin.cn/n/740279.html

相关文章

注解进行代码测试

1.创建注解类 package ceshi; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; Retention(RetentionPolicy.RUNTIME) public interface Jiecha { }2.创建类并为方法添加注解 package ceshi; import ceshi.Jiecha; public class NoBug …

图论学习加餐部分

加餐部分 – 潘登同学的图论笔记 文章目录加餐部分 -- 潘登同学的图论笔记加餐第一题&#xff1a; 骑士周游问题DFS实现加餐第二题&#xff1a; 词梯问题BFS实现加餐第三题&#xff1a; 强连通分支&#xff08;kosaraju算法&#xff09;加餐第四题&#xff1a; 图的最短路径算法…

树的数据结构、最小支撑树(上)

树的数据结构、最小支撑树&#xff08;上&#xff09; – 潘登同学的图论笔记 文章目录树的数据结构、最小支撑树&#xff08;上&#xff09; -- 潘登同学的图论笔记无向树二叉树森林无向树理论部分无向树的等价定义支撑树最小支撑树构造最小生成树的算法Prim算法算法实现Krusk…

centos7.6设置静态IP

1.找到虚拟机的网关IP 路径&#xff1a;编辑–虚拟机网络编辑器–NAT设置 2.配置本地网络适配器 如VMnet8 3.配置虚拟机网络配置文件,可以用命令查看虚拟机用了哪个网络适配器 网络配置文件路径&#xff1a;cd /etc/sysconfig/network-scripts vi ifcfg-ens33 TYPE"E…

sql查看job的语句

select * from msdb.dbo.sysjobs select * from msdb.dbo.sysjobsteps

最小支撑树(下)

最小支撑树&#xff08;下&#xff09; – 潘登同学的图论笔记 文章目录最小支撑树&#xff08;下&#xff09; -- 潘登同学的图论笔记破圈法&#xff08;Ⅰ&#xff09;算法实现破圈法(Ⅱ)算法实现博鲁夫卡算法算法实现最小瓶颈支撑树斯坦纳树书接上文&#xff0c;上一篇我们写…

sql server创建流水号 年月日+流水号

Create function [dbo].[f_GetFxNum]() returns varchar(15) as begin declare FxNum varchar(15) declare time varchar(8) set timeCONVERT(varchar,YEAR(GETDATE()))RIGHT(00CONVERT(varchar,month(getdate())),2)RIGHT(00CONVERT(varchar,DAY(getdate())),2)--取到当前年月…

Adaboost 算法与集成学习

Adaboost 算法与集成学习 – 潘登同学的Machine Learning笔记 文章目录Adaboost 算法与集成学习 -- 潘登同学的Machine Learning笔记BoostingAdaboost如何生成g(x)g(x)g(x)Adaboost 中的数据权重 Un目标更新Uit1U_i^{t1}Uit1​迭代每一轮物理权重Uit1U_i^{t1}Uit1​时的方式合并…