算法学习--图论

最短路问题

最短路问题最关键的概念是松弛

单源最短路径

bellman-ford算法

在这里插入图片描述
d[v]表示从源点到v的当前最短路径长度,先将所有d[v]初始化为无穷,再将源点的d[s]标记为0,然后对循环v-1次,每一次对所有边进行一次松弛操作,循环结束后检查是否还有边可以松弛,若有代表图中有负权回路,算法无解

算法的适用范围

无负权环,可以有负权边

算法的时间复杂度

o(VE)

算法的正确性证明

在这里插入图片描述
在这里插入图片描述

考虑求v号节点的最短路径值:当前图中必定存在一条从源点s到v号节点的最短路径path
假设是从 s → v 1 → v 2 → v 3... s \to v_1 \to v_2 \to v3 ... sv1v2v3...在上述例子中我们可以看s到z的最短路径是 s → y → x → t → z s \to y \to x \to t \to z syxtz下面证明每一轮的松弛操作后,s到z的最短路径上的 d [ v i ] d[v_i] d[vi]被更新且 d [ v 1 ] , d [ v 2 ] , d [ v 3 ] . . . d [ v i − 1 ] d[v_1] ,d[v_2],d[v_3]...d[v_i-1] d[v1],d[v2],d[v3]...d[vi1]在当前松弛操作下不发生变化

  1. 由于path是从s到z的最短路径,则对于path上的每一个点y,x,t,z,s沿着path到其距离也是y,x,t,z的最短路径距离,因为最短路问题具有优化子结构
  2. 因为path是s到z的最短路径,则第一轮更新后 d [ v 1 ] d[v_1] d[v1]也就是d[y]一定等于s到y的最短路径长度
  3. 一方面,第二轮更新后d[y]值不会改变,因为假设d[y]值改变,意味着存在u使得d[u]+w(u,y) ≤ \leq d[y]这样s到u再沿(u,y)到y的路径长度小于d[y],与s到y已经是最短路径长度矛盾;另一方面,第二轮更新中一定松弛(y,x)这条边而更新d[x],且d[x]最后的值一定等于d[y]+w(y,x),因为path是s到x的最短路径
  4. 由以上分析可知,每一轮松弛操作会确定最短路上的一个节点的d[v],且s到z的节点数不超过v-1个(总节点数为v,不包含s本身),则v-1轮后所有节点的d[v]值都正确

bellman-ford算法的应用-K站中转内最便宜的航班

问题的定义

有 n 个城市通过 m 个航班连接。每个航班都从城市 u 开始,以价格 w 抵达 v。现在给定所有的城市和航班,以及出发城市 src 和目的地 dst,你的任务是找到从 src 到 dst 最多经过 k 站中转的最便宜的价格。 如果没有这样的路线,则输出 -1。
提示:
n 范围是 [1, 100],城市标签从 0 到 n - 1.
航班数量范围是 [0, n * (n - 1) / 2].
每个航班的格式 (src, dst, price).
每个航班的价格范围是 [1, 10000].
k 范围是 [0, n - 1].
航班没有重复,且不存在环路

问题的求解

应用bellman-ford算法,最多经过k站中转等价于循环k+1次,由算法的正确性证明可知,若存在这样的路线,则k+1次松弛后d[dst]会确定并正确,若不存在这样的路线,k+1次松弛后d[dst]仍未被更新

问题的伪代码

需要注意的是我们的转机数要小于等于k,这就使得我们在一轮松弛操作中不能确定两个及以上的d[v]
比如我们在上一次循环中确定了d[a],src到dst上存在最短路a->b->c,在这一轮松弛中我们先松弛了a->b这条边使得d[b]被确定,再松弛了b->c这条边使得d[c]被确定,即我们在一轮循环中确定了2个及以上的中转站,使得我们在k+1轮循环后最终找到的src到dst的最短路径上可能有大于k个中转站,对于这一情况,我们需要在每次循环时拷贝一个上一次循环后每个点的d[v]的副本,保证在这一次松弛时每条边的出度点的d[v]是按上一次循环后确定的

Bellman-Ford(G,src,dst)
	n<-G.node;
	flights<-G.edge;
	for i<-0 to n-1 do
		d[i]<-inf;
	d[src]<-0;
	for i<-0 to k do
		for i<-0 to n-1 do
			cost[i]<-d[i];//拷贝副本
		for each edge(u,v) in flights do
			if(cost[v]>d[u]+w(u,v)) do
				cost[v]<-d[v]+w(u,v);
		for i<-0 to n-1 do
			d[i]<-cost[i];
	return d[dst]==inf? -1:d[dst];	 
			
有向无环图中BF算法的优化

使用拓扑排序

dijkstra算法

在这里插入图片描述
S标注当前已经确定了最短路径的集合,Q是一个以d[V]为键的优先队列,Q=V-S,,当把u从Q中取出时,表示u为当前与S中节点相连的节点中距离最小的节点,将u添加到S中,再对u的每一条出度边进行松弛,循环上述操作直到所有节点都在S中

算法的使用范围

无负权图
为什么不能包含负权?若包含负权,则u从Q中取出时,它还不一定收敛,即还不一定找到源点到u的最短路径

算法的时间复杂度
算法的正确性证明

在这里插入图片描述

运用数学归纳法:对算法执行的循环次数n进行归纳
*数学符号含义:
d[v]表示v号节点与源节点的最短路径长度
u为第k+1号节点,在V-S中
w(u,v)表示节点u和v之间的权值
证明:

  1. n=1时,w(s,s)=0,故d[s]=0由算法的初始化得到
  2. 假设n=k时,前k步得到的S中的K+1个节点的最短路径已经知道并可用d[ v i v_i vi]获得, 1 ≤ i ≤ k 1\leq i \leq k 1ik
  3. 当n=k+1时,下面证明算法得到的 ∗ d [ u ] *d[u] d[u]确实是s到u的最短路径:
  • 假设path为s到u的最短路径,若path中u的上一号节点y在S中,根据归纳S中的所有节点的最短路径已经被确定且可用d[ v i v_i vi]获得,而算法又是搜索和S中节点直接相连的具有最短距离的节点u, ∗ d [ u ] = m i n ( d [ v i ] + w ( v i , u ) , d [ u ] ) *d[u]=min(d[v_i]+w(v_i,u),d[u]) d[u]=min(d[vi]+w(vi,u),d[u])且此时的d[u]是上一轮已经更新过的u到s的最短距离,所以此时算法第n步得到的*d[u]确实是源s到u的真实最短路径长度
  • 假设y是path中第一个存在于V-S中的节点,则 ∃ x ∈ S , y ∈ V − S , s t . ∗ d [ u ] = d [ y ] + δ ( u , v ) 又 δ ( y , u ) ≥ 0 , 故 ∗ d [ u ] > d [ y ] 【 d [ y ] = m i n ( d [ v i ] + w ( v i , y ) , d [ y ] ) 】 \exists x \in S ,y \in V-S ,st. *d[u]=d[y]+\delta(u,v) 又\delta(y,u) \geq 0,故*d[u] \gt d[y]【d[y]=min(d[v_i]+w(v_i,y),d[y])】 xS,yVS,st.d[u]=d[y]+δ(u,v)δ(y,u)0,d[u]>d[y]d[y]=min(d[vi]+w(vi,y),d[y]) ∃ y ∈ V − S , s t . d [ y ] < ∗ d [ u ] \exists y \in V-S ,st. d[y] \lt *d[u] yVS,st.d[y]<d[u]那么算法在选取第k+1个点时应该选取y而不是u,产生矛盾
综上,当n=k+1时,算法得到的*d[u]确实是s到u的最短路径

多源最短路径

floyd算法

运用动态规划思想
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

算法的伪代码

在这里插入图片描述
p矩阵用于存路径

网络流-最大流问题

问题的定义

假设现在有一个地下水管道网络,有m根管道,n个管道交叉点,现在自来水厂位于其中一个点,向网络中输水,隔壁老王在另外一个点接水,已知由于管道修建的年代不同,有的管道能承受的水流量较大,有的较小,现在求在自来水厂输入的水不限的情况下,隔壁老王能接到的水的最大值?

问题的求解

Ford-Fulkerson算法伪代码
在这里插入图片描述

匹配问题

问题的定义

在这里插入图片描述

在这里插入图片描述

匈牙利算法求二分图的最大匹配伪代码

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值