赞
踩
问题描述:
给定带权重的有向图G=(V,E)和权重函数w,能够将每条边映射到实数值得权重上。
给定一个源点,希望求出它到所有节点的最小权重路径。
定义路径的权重:
该路径上所有边的权重之和
定义最短路径权重:
最短路径问题具有最优子结构:
即最短路径的子路径也是最短路径
本质上还是反证法的思想
负权重的边:
1.Bellman-Ford算法允许图中有负权环;并且能够检测负权回路的存在
2.Dijkstra算法要求边非负。
环路:
1、最短路径不能包含权重为负值的环——否则最短路径可能会导致-∞
2、最短路径不能包含权重为正值的环——如果包含这样的环,那么我们删掉这个环,可以得到一个更小的最短路径
因此,最短路径中没有环路,是简单路径。
因此,一条最短路径最多包含V个结点,|V|-1条边。
展示最短路:
需要为每个结点维持一个前驱结点Π,Π=NIL或为另一个结点。
由此,我们可以定义出
V
π
和
E
π
V_\pi和E_\pi
Vπ和Eπ:
G
π
G_\pi
Gπ是一棵最短路径树,包含了从源节点s到每个可以到的顶点的一条最短路径。
最短路径不一定是唯一的,最短路径树也不一定是唯一的。
松弛操作:
为每个结点维持另一个属性d,用来记录从s到该结点的最短路径权重的上界,称d为最短路径估计。
松弛:唯一导致最短路径估计d和前驱节点
π
\pi
π发生变化的操作
Dijkstra:对每条边仅松弛一次
Bellman-Ford:每条边松弛|V|-1次
初始化:
为所有结点初始化d和
π
\pi
π两个属性:
一些性质:
允许权重为负值的边
通过对边进行松弛操作降低所有顶点的最短路径估计d,并更新
π
\pi
π值,直到d=最短路径权重。
除此之外,算法会返回一个布尔值来表明图中是否存在负权环。
BELLMAN-FORD(G,w,s)
INITIALIZE-SINGLE-SOURCE(G,s)
for i = 1 to G.|V|-1 //因此最短路径的长度至多为|V|-1
for each edge (u,v) ∈ G.E
RELAX(u,v,w)
for each edge(u,v) ∈ G.E //检测负权环
if v.d > u.d + w(u,v)
return FALSE
return TRUE
T ( n ) = Θ ( V ) + Θ ( E ( V − 1 ) ) + O ( E ) = O ( V ⋅ E ) T(n)=\Theta(V)+\Theta(E(V-1))+O(E)=O(V·E) T(n)=Θ(V)+Θ(E(V−1))+O(E)=O(V⋅E)
先对有向无环图进行拓扑排序,确定每个结点之间的一个线性次序。如果有u到v的一条边,那u的次序在v前面。
我们只需要按照拓扑排序对结点进行一遍处理,每次对一个节点进行处理时,对该结点发出的所有边进行松弛操作
DAG-SHORTEST-PATHS(G,w,s)
1 topologically sort the vertices of G
2 INITIALIZE-SINGLE-SOURCE(G,s)
3 for each vertex u,taken in topologically sorted order
4 for each vertex v that u adjacent to //可以建立一个邻接数组,adj[u]
5 RELAX(u,v,w)
时间复杂度:
要求所有边权重非负。
每次选择一个“最短”的路径——贪心算法
DIJKSTRA(G,w,s)
INITIALIZE-SINGLE-SOURCE(G,s) //初始化
S = 空集
Q = G.V //始终满足Q=V-S
While Q != 空集
u = EXTRACT-MIN(Q) //u是未加入结点中最小最短路估计
S = S ∪ {u}
//如果经过u的路径使s到v更短,则更新v的属性
for each vertex v ∈ G.adj[u]
RELAX(u,v,w)
Dijkstra算法的实现依赖于最小优先队列。
1.利用编号结点来维持最小优先队列:EXTRACT-MIN需要
O
(
V
)
O(V)
O(V),搜索整个数组,
T
(
n
)
=
O
(
V
2
+
E
)
=
O
(
V
2
)
T(n)=O(V^2+E) = O(V^2)
T(n)=O(V2+E)=O(V2)
2.使用斐波那契堆实现最小优先队列:
EXTRACT-MIN需要
O
(
l
g
V
)
O(lgV)
O(lgV),
T
(
n
)
=
O
(
V
l
g
V
+
E
)
T(n)=O(VlgV+E)
T(n)=O(VlgV+E)
不再给出源点,需要求所有结点对之间的最短路径,实际上就是|V|次单源最短路,但这样做效率很低。
为了求出问题,不仅需要计算最短路权重,还需要计算出前驱结点矩阵 Π = ( π i j ) \Pi=(\pi_{ij}) Π=(πij), π i j \pi_{ij} πij在i=j或不存在i到j路径时为NIL,否则给出的是从i到j的最短路上j的前驱结点。
同样,我们可以定义一个最短路径树 G π , i G_{\pi,i} Gπ,i,只是这里多了一个参数–我们需要给出根节点i:
PRINT-ALL-PAIRS-SHORTEST-PATH(Π,i,j)
if i == j
print i
elseif Π_ij == NIL
print "no path from i to j exists"
else PRINT-ALL-PAIRS-SHORTEST-PATH(Π,i,Π_ij)
print j
下面给出三种解决方法
1、基于矩阵乘法的动态规划算法——
Θ
(
V
3
l
g
V
)
\Theta(V^3lgV)
Θ(V3lgV)
2、Floyd-Warshll算法(动态规划)——
Θ
(
V
3
)
\Theta(V^3)
Θ(V3)
3、Johnson算法——
O
(
V
2
l
g
V
+
V
E
)
O(V^2lgV+VE)
O(V2lgV+VE)
定义
l
i
j
(
m
)
l_{ij}^{(m)}
lij(m)表示从i到j至多包含m条边的任意路径的最小权重,
当
m
=
0
m=0
m=0,
当
m
≥
1
m\ge1
m≥1,
我们考虑图中不含负权环,而最短路最多n-1条边,因此
最小路径权重
=
l
i
j
(
n
−
1
)
=
l
i
j
(
n
)
=
l
i
j
(
n
+
1
)
=
.
.
.
最小路径权重=l_{ij}^{(n-1)}=l_{ij}^{(n)}=l_{ij}^{(n+1)}=...
最小路径权重=lij(n−1)=lij(n)=lij(n+1)=...
输入矩阵
W
i
,
j
=
(
w
i
j
)
W_{i,j}=(w_{ij})
Wi,j=(wij),且
L
(
1
)
=
W
L^{(1)}=W
L(1)=W
通过对最短路径一条边一条边的扩展来计算最短路权重。
EXTENDED-SHORTEST-PATHS(L,W)
n = L.rows
let L' = (l_ij') be a new n*n matrix
for i = 1 to n
for j = 1 to no
l_ij' = ∞
for k =1 to n
l_ij' = min(l_ij',l_k+w_kj)
return L'
返回矩阵L’=(l_ij’)
T
(
n
)
=
Θ
(
n
3
)
T(n)=\Theta(n^3)
T(n)=Θ(n3)
SLOW-ALL-PAIRS-SHORTEST-PATHS(W)
n = W.rows
L(1) = W
for m = 2 to n-1
Let L(m) be a new n*n matrix
L(m) = EXTENDED-SHOERTEST-PATH(L(m-1),W)
return L(n-1)
这样算
T
(
n
)
=
Θ
(
n
4
)
T(n)=\Theta(n^4)
T(n)=Θ(n4)
我们考虑改进算法:
由我们刚刚推导出的
最小路径权重
=
l
i
j
(
n
−
1
)
=
l
i
j
(
n
)
=
l
i
j
(
n
+
1
)
=
.
.
.
最小路径权重=l_{ij}^{(n-1)}=l_{ij}^{(n)}=l_{ij}^{(n+1)}=...
最小路径权重=lij(n−1)=lij(n)=lij(n+1)=...,我们可以采用重复平方技术来减小时间复杂度:
FASTER-ALL-PAIRS-SHORTEST-PATHS(W)
n = W.rows
L(1) = W
m = 1
while m <= n
Let L(2m) be a new n*n matrix
L(2m) = EXTENDED-SHORTEST-PATHS(L(m),L(m))
return L(m)
最后的结果
L
(
m
)
=
L
(
n
−
1
)
L^{(m)} = L^{(n-1)}
L(m)=L(n−1)
一共计算了lg(n-1)取上整个矩阵,每个矩阵时间为
Θ
(
n
3
)
\Theta(n^3)
Θ(n3),因此
T
(
n
)
=
Θ
(
n
3
l
g
n
)
T(n)=\Theta(n^3lgn)
T(n)=Θ(n3lgn)
允许负权重的边存在。
动态规划算法。
设
d
i
j
(
k
)
d_{ij}^{(k)}
dij(k)为从结点i到j的所有中间结点全部取自{1,2,…,k}的一条最短路的权重。
FLOYD-WARSHALL(W)
n = W.rows
D(0) = W
for k = 1 to n
let D(k) = (d_ji^k)be a new n*n matrix
for i = 1 to n
for j = 1 to n
d_ij^k = min(d_ij^(k-1),d_ik^(k-1)+d_kj^(k-1))
return D(n)
三层循环 T ( n ) = Θ ( n 3 ) T(n) = \Theta(n^3) T(n)=Θ(n3)
这里只返回了权重矩阵,仍需构建一条最短路,需要构建前驱矩阵:
1、先计算权重矩阵D,从D构造前驱矩阵
Π
\Pi
Π,再用PRINT-ALL-PAIRS-SHORTEST-PATH打印最短路径,
T
(
n
)
=
O
(
n
3
)
T(n)=O(n^3)
T(n)=O(n3)
2、计算
D
(
k
)
D^{(k)}
D(k)的同时计算前驱矩阵
Π
\Pi
Π,定义
Π
=
Π
(
n
)
且
Π
i
j
(
k
)
\Pi=\Pi^{(n)}且\Pi_{ij}^{(k)}
Π=Π(n)且Πij(k)为从i到j的一条所有中间结点取自{1,2,…,k}的最短路径上j的前驱节点
传递闭包
定义:传递闭包为图
G
∗
=
(
V
,
E
∗
)
G^*=(V,E^*)
G∗=(V,E∗),其中
E
∗
=
i
,
j
:如果图
G
中有一条
i
到
j
的路径
E^*={i,j:如果图G中有一条i到j的路径}
E∗=i,j:如果图G中有一条i到j的路径。
我们定义
t
i
j
(
k
)
=
1
t_{ij}^{(k)}=1
tij(k)=1代表图G中存在一条从i到j的路径,其中间结点均属于{1,2,…,k}中,否则
=
0
=0
=0.那么传递闭包
G
∗
=
(
V
,
E
∗
)
G^*=(V,E^*)
G∗=(V,E∗):将边(i,j)置于
E
∗
中
⟺
t
i
j
(
j
)
=
1
E^*中\iff t_{ij}^{(j)}=1
E∗中⟺tij(j)=1
TRANSITIVE-CLOSURE(G)
n = |G.V|
let T(0) = (t_ij^0) be a new n*n matrix
for i = 1 to n
for j = 1 to n
if i==j or (i,j)∈E
t_ij^0 = 1
else t_ij^0 = 0
for k = 1 to n
let T(K) = (t_ij^k) be a new n*n matrix
for i = 1 to n
for j = 1 to n
t_ij^k = t_ij^(k-1) ∨ (t_ik^(k-1) ∧ t_kj^(k-1))
return T(n)
T
(
n
)
=
Θ
(
n
3
)
T(n) = \Theta(n^3)
T(n)=Θ(n3)
最后得到的结果实际上是个可达矩阵,因为得到的路径它中间能够包含所有结点。若第i行第j列为1,就代表着存在着从i至j的一条路径。
在这个算法中使用到了Dijkstra算法和Bellman-Ford算法:
1、它会返回包含所有结点对的最短路径权重的矩阵;
2、并报告输入图中是否存在负权环路
Johnson算法为边重新赋予权重:
如果图G中所有边权重非负,可以通过对每个结点运行一次Dijkstra算法解决问题;若使用斐波那契堆最小优先队列,
T
(
n
)
=
O
(
V
2
l
g
V
+
V
E
)
T(n)=O(V^2lgV+VE)
T(n)=O(V2lgV+VE);
如果G中有负权重边,但没有负权环路,那么只要计算出一组新的非负权重值,然后使用Dijkstra算法。新赋予的权重
u
^
\hat{u}
u^必须满足两个性质:
1、不改变最短路径的性质
2、新权重非负
JOHNSON(G,w)
若使用斐波那契堆实现Dijkstra中的最小优先队列,算法
T
(
n
)
=
O
(
V
2
l
g
V
+
V
E
)
T(n)=O(V^2lgV+VE)
T(n)=O(V2lgV+VE)
若使用二叉最小堆,
T
(
n
)
=
O
(
V
E
l
g
V
)
T(n)=O(VElgV)
T(n)=O(VElgV)
流网络G=(V,E)是一个有向图,每条边有一个非负容量值
c
(
u
,
v
)
≥
0
c(u,v)\ge0
c(u,v)≥0,并且若E中有边(u,v),则不含其反向边(v,u).
流网络中有一个源节点s,汇点t。若对于每个结点来说,都有一条从s经过v到t的路径,那我们说流网络是流通的。
除s外,每个结点都至少有一条进入边,因此
∣
E
∣
≥
∣
V
∣
−
1
|E|\ge |V|-1
∣E∣≥∣V∣−1
流:满足性质:容量限制 & 流量守恒
容量限制:对所有结点v,要求流的大小不超过容量大小
流量守恒:对所有除s、t之外
的结点,要求从结点流出的总流量=流入结点的总流量和
流的大小:
从源点流出的总流量-流入源点的总流量
关键思想:残存网络、增广路径、切割
构建残存网络,寻找一条增广路径,修改流的值,在残存图中添加反向边;循环此过程直到找不到增广路径为止。
总的来看,每次迭代都会增加流的值,但对于G中的一条特定边来说,其流量可能增加也可能减少,这是因为对某些边缩减是有必要的,这可能会让更多的流从源点发送到汇点。
这种算法能够获得一个最大流
。
一、残存网络
残存网络:
定义一条边可以允许额外流量=该边容量-该边上的流量,称之为残存容量
残存网络 G f = ( V , E f ) , E f = ( u , v ) ∈ V ∗ V : c f ( u , v ) > 0 G_f=(V,E_f),E^f ={ (u,v)∈V*V:c_f(u,v)>0 } Gf=(V,Ef),Ef=(u,v)∈V∗V:cf(u,v)>0,也就是说残存网络中的每条边或残存边都必须允许大于0的流量通过; E f E_f Ef中的边要么是E中所有边,要么是其反向边,因此有 ∣ E f ∣ ≤ 2 ∣ E ∣ |E_f|\le 2|E| ∣Ef∣≤2∣E∣
需要厘清,残存网络并不是流网络,因为他不满足流网络的定义——他可能包含边(u,v)及其反向边(v,u)。
除此之外,它具有和流网络同样的性质。
二、增广路径
增广路径:
残存网络中一条从源点s到汇点t的简单路径。
每条这样的路径它的流量值决定于路径上所有边的最小残存容量。
三、切割
流网络中的一个切割(S,T)将V划分为S和T两个集合,T=V-S,且源点s∈S,汇点t∈T。
若f是一个流,则定义横跨切割(S,T)的净流量f为:从集合S中流出的总流量-流入集合S的总流量
切割(S,T)的容量是从集合S到集合T所有路径的总容量
我们只关注
最小切割
——整个网络中容量最小的切割
对于给定的流f,横跨任何切割的净流量都相同,都等于|f|
流网络中,任意流f的值都不能超过任意切割的容量
最大流最小割定理:
FOLD-FULKERSON-METHOD(G,s,t)
for each edge(u,v)∈G.E
(u,v).f = 0 //初始化流f=0
while there exists a path p from s to t in the residual network Gf
c_f(p) = min {c_f(u,v):(u,v) is in p} //找到路径p的瓶颈值
for each edge (u,v) in p
//对流进行更新,若残存边是G中边,则加流量,否则减流量
if (u,v) ∈ E
(u,v).f = (u,v).f + c_f(p)
else (v,u).f = (v,u).f - c_f(p)
运行时间取决于如何寻找增广路径
:
如果使用深度优先/广度优先搜索,在残存网络中找到一条路径的时间应是
O
(
V
+
E
′
)
=
O
(
E
)
O(V+E') = O(E)
O(V+E′)=O(E),因此while每一遍执行所需O(E),而while最多循环次数是
∣
f
∗
∣
|f^*|
∣f∗∣次,所以
T
(
n
)
=
O
(
E
∣
f
∗
∣
)
T(n)=O(E|f^*|)
T(n)=O(E∣f∗∣)
改变FOLD-FULKERSON算法,使要找的增广路径为从s到t的最短路径,其中每条边的权重为单位距离。
这样,算法时间为
O
(
V
E
2
)
O(VE^2)
O(VE2)
使用FOLD-FULKERSON 算法能在 O ( V E ) O(VE) O(VE)时间内解决图G=(V,E)的最大二分匹配问题。
二分图:
构建一个流网络,其中流对应匹配。
我们设源节点s和汇点t是不属于结点集V的新节点,并且V’=V ∪ {s,t}
|M|——二分图中能匹配的最多结点对个数
利用FOLD算法在G’中找到一条最大流f,那么G中就存在一个匹配M,满足|M| = |f|
基础定义:P、NP、NPC
下面主要是一些常见的NPC问题。
电路可满足性问题——第一个证明的NPC问题。
寻找图中规模最大的团的最优化问题。
实际上就是找图G中最大的完全子图。
证明团问题是NPC问题:证明 3 − C N F − S A T ≤ p C L I Q U E 3-CNF-SAT\le_p CLIQUE 3−CNF−SAT≤pCLIQUE
给出一个图G,找到一个最小的顶点集合,使其能够覆盖G中每条边
证明顶点覆盖问题是NPC问题:证明 C L I Q U E ≤ p V E R T E X − C O V E R CLIQUE\le_p VERTEX-COVER CLIQUE≤pVERTEX−COVER
给出一个图G,找出一条回路,使其能够恰经过所有顶点仅1次。
证明哈密顿回路是NPC问题:证明 V E R T E X − C O V E R T ≤ p H A M − C Y C L E VERTEX-COVERT\le_p HAM-CYCLE VERTEX−COVERT≤pHAM−CYCLE
经过哈密顿回路,恰好访问每个城市一次,并回到出发城市。
除此之外,要求费用最小——每条路的权重之和
证明旅行商问题是NPC问题:证明 H A M − C Y C L E ≤ p T S P HAM-CYCLE\le_p TSP HAM−CYCLE≤pTSP
给出一个有限正整数集合S和一个整数目标t>0,找到一个字集合,使其所有元素和恰好为t。
证明子集和问题是NPC问题:证明 3 − C N F − S A T ≤ p S U B S E T − S U M 3-CNF-SAT\le_p SUBSET-SUM 3−CNF−SAT≤pSUBSET−SUM
所有证明过程在此处省略。
近似算法:返回近似最优解的算法
近似比:
一些NPC问题可以采用特定的多项式时间近似算法求解,通过消耗更多的计算时间来得到不断缩小的近似比;另一些问题,在已知的最佳多项式时间近似算法中,近似比是输入规模n的函数,随着n的变化而变化。
输入除了问题的实例外,还有一个值 ϵ > 0 \epsilon > 0 ϵ>0,使得对于任何固定 ϵ \epsilon ϵ,该模式是一个 ( 1 + ϵ ) (1+\epsilon) (1+ϵ)近似算法。
对一个近似模式来说,如果对任何固定的 ϵ > 0 \epsilon > 0 ϵ>0,该模式都以输入实例规模n的多项式时间运行,则称此模式为多项式时间近似模式
随着 ϵ \epsilon ϵ的减小,多项式时间近似模式的运行时间可能会迅速增长
完全多项式时间近似模式
下面给出一些NPC问题的近似算法。
规模不超过 最优顶点覆盖规模2倍
的顶点覆盖
例:
以邻接表
来表示E,算法的运行时间为
O
(
V
+
E
)
O(V+E)
O(V+E)
满足三角不等式:
两地之间直接到达的代价最小
我们考虑满足三角不等式的旅行商问题
,近似算法代价不超过最优的2
倍
例:
一般旅行商问题,即没有满足三角不等式的假设,那么问题就无法在多项式时间内找到一个好的近似算法,除非P=NP
给出一个有穷集X,和它的一个子集族,每个元素至少在一个子集中,我们希望找到一个最小数量的子集集合,使其能覆盖X中所有元素。
贪心近似算法:
近似比是
l
n
∣
X
∣
+
1
ln|X|+1
ln∣X∣+1
在顶点覆盖问题中,每个顶点都具有一个正权值,希望找出一个具有最小权的顶点覆盖
利用线性规划松弛,构造近似解
该算法是一个近似比为2
的近似算法
指数时间的近似算法
完全多项式时间近似模式
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。