网络流概览
1.网络流(Network Flow)概念
网络流起源于对于交通网络的分析,举一个非正式的例子,网络流算法的目的就是找到带权有向图中从源点到汇点的traffic的大小。我们的目标是在多项式时间内解决问题。
我们一般要求的是最大流,如果把源点比作工厂的话,问题就是求从工厂最大可以发出多少货物,是不至于超过道路的容量限制,也就是——最大流。
2.网络流的一些定义
流量网络:给一个有向图
G=(V,E)
,每一条边
e
都有一个非负数的容量
源点:图中有n个点,有m条有向边,有一个点 s 很特殊,只有出边没有进边,叫做源点。
汇点:另一个点
容量和流量:在图中的每条有向边上有两个量,容量
c
和流量
举个例子:通常可以把这些边想象成道路,流量就是这条道路的车流量,容量就是道路可承受的最大的车流量。很显然的,流量<=容量。而对于每个不是源点和汇点的点来说,可以类比的想象成没有存储功能的货物的中转站,所有“进入”他们的流量和等于所有从他本身“出去”的流量。
s−t
flow:是一个为每一条边分配非负流量值
f(e)
的函数,这里我们需要满足两个条件:
1.容量限制(Capacity conditions):对于每条边
e
,
2.平衡限制(Conservation conditions):对于每一个非
s
和
value of a flow:从源点
s
流出的流量的综合。
从源点s出发的可以有多条边,这里指的是整个图的总流量。
最大流:如果把源点比作工厂的话,最大流问题就是求从工厂最大可以发出多少货物,是不至于超过道路的容量限制。Flow Network中的Maximum Flow value,这就是最大流的定义。
Residual graph: 由图 G中的流派生而来,记作
-
Gf
has the vertices of G
-
Gf
has two kinds of edges: for each edge
e=(u,v)∈E(G)
- Forward edges: if
e
has leftover capacity. Gf contains the edge
- **Backward edges:**if e has positive flow
Gf
contains the edge
(v,u)
with capacity
f(e)
augmenting path: 是一条在剩余图
Gf
中的
s−t
的路径。
• The bottleneck of a path
is the minimum residual capacity of all edges in that path
• To augment flow f using path P consists of modifying the flow in all edges of P as follows:
◮ let
b=
bottleneck of P
◮ increase the flow by b in all forward edges of P
◮ decrease the flow by b in all backward edges of P
3.
4.
动态规划的经典问题
1.加权间隔调度
输入:一组n个间隔,没个时间段有开始时间、结束时间以及时间段的权重。
输出:不互相重叠的一组具有最大权重的间隔集。
Eg:
解决方法:
通过“带记忆”的储存加递归的方式解决。
int Memoized Opt(j)
if j = 0 then return(0)
else
if M[j] = "undefined" then
M[j] ← max v(j) + Opt(p(j)), Opt(j − 1)
return M[j]
将间隔按照结束时间排序,第j个间隔时的权重和应该为:j的权重与上一个不与第j个间隔重合的间隔的Opt(p(j))的和;与Opt(j-1)相等。当中最大的一个。
也可以自底向上的用递推的方式计算
IterativeComputeOpt
M[0] ← 0
for j ← 1 to n do
M [j] ← max v(j) + M [p(j)], M [j − 1]
2.分段最小二乘问题
给出一组n个点,需要确定一条能够最好的拟合(fit)这些点的直线。有error = 点到直线的距离的平方,而这条直线具有最小的error。
使用多条直线能够做到更好的拟合,但是分成越多的段对于我们的数据分析没有意义。因为有指数中不同的划分(相当于求子集),使用暴力搜索是不现实的。
解决方法:
我们引入一个可变的变量C,作为算法的惩罚值,拟合划分的线越多,C值越大。因此我们的目标就是找到最小的:C + 各条线的error值。
定义e(i,j) 是拟合pi到pj这些点时的error;OPT(i)为pi到pj的最优解(OPT(0) = 0)。此时可知:
我们无法确定i的值,但是可以选择能够给出最小值的i的值。
由此我们可以得到伪代码:
计算每个点的分段最小二乘法的error值,然后储存在数组中,之后遍历数组来获得最好的拟合线。
SegmentedLeastSquares(n)
Array M[0...n]
M[0] ← 0
for all pairs (i, j)
compute least squares error[i, j] for segment pi . . . pj
for j ← 1 to n
M [j ] ← min1≤i≤j (error[i, j ] + C + M [i − 1] )
Find Segments(j)
find i that minimizes error[i,j]+C+M[i−1]
if i > 1 then Find Segments(i − 1)
output the best fit line for segment pi...pj
3.汽车装载问题
要求尽可能多的向汽车上装载货物。
输入:n个箱子,每一个箱子i有重量wi;汽车的总承重量为W。
输出:在满足总重量限制下,所能尽可能多的装载重量的箱子的子集。
解决方法:
很明显,在这种情况下,贪心算法并不能给出最优解。比如汽车总承重量为10,有4个箱子,分别重量为8、3、3、3。最优解为9,但贪心只会选择一个重量为8的箱子。
我们可以通过减少箱子数量和减少总重量限制来将问题化解为更小的问题。我么用OPT(i, w)表示我们在重量限制为w条件下,在前i个箱子中所能装载的最大重量。
对于第i个箱子,我们可以选择装载它或者不装载它,则有:
应注意,wi不能大于w,否则只能选择不装载箱子i。此算法有时间复杂度:O(n·W)。
4.背包问题(Knapsack Problem)
输入:n件物品,物品i有重量wi和价值vi;背包总重量W。
输出:一组在满足总重量限制下的具有最大价值的物品集合。
解决方法:
此时贪心也无法得到最优解,例如W = 100,有两个物品,重量与价值分别为(20, 80)、(90, 90),此时贪心会选择最小重量,但是背包已经装不下了。
背包问题就像是引入了价值的卡车装载问题。令OPT(i, w)表示在前i个物品中,在w重量限制下,所能取得的最大价值。我们可以选择要不要这件物品,则有:
应注意,wi不能大于w,否则只能选择不装载物品i。此算法有时间复杂度:O(n·W)。
4.序列对比(Sequence Alignment)
为了确定两个序列之间的相似性,将两个string按照一定的规律排列。序列中可以通过插入间隔来获得更好的相似性,但我们会为gap和mismatch分配不同的惩罚值,gap是有cost的。给出两个string,计算它们之间的相似程度
解决方法:
设两个string为x和y,我们令A(i, j)表示对于x1到xi,y1到yj的最佳匹配值,即最小的gap与mismatch惩罚值。则有:
伪代码:
ALIGN( X[1:m],Y[1:n] )
for j ← 0 to n
A[0,j] ← jδ
for i ← 1 to m
A[i, 0] ← iδ
for j ← 1 to n
A[i,j]←min{αxi,yj +A[i−1,j−1], δ+A[i−1,j],
δ+A[i,j−1] }
5.带负边的最短路径(Bellman-Ford Shorest Path Algorithm)
输入:带权有向图G,c(v, w)表示从顶点v到w的边的cost,可以为负。
输出:一个负环(所有边的cost之和为负)或者一条从s到t的最短路径。
实际生活中,比如经济学中的互相转账就有可能会引入负边,然而Dijkstra算法因此受限,无法解决。这时就应当使用Bellman-Ford Shortest Path Alg,使用前我们需要知道一条定理:如果有向图没有负环,那么对于有n个点的图G,从点s到t的最短路径至多含有n-1条边。
解决方法:
令OPT(i, v)表示在小于等于i条边的情况下,从点v到点t的最小path cost。
我们要求的是OPT(n - 1, s),有递归初始条件OPT(0, t) = 0,OPT(0, v) = +∞(v !=
t)。则有:
伪代码:
Shortest-Path(G, s, t)
n ← number of nodes in G
for all v except t, initialize M[v] ← +∞
M[t] ← 0
for i←1 to n−1
for all e=(v,w)∈E(G)
M [v] ← min(M [v], c(v, w) + M [w])
return M[s]
可以通过在计算OPT(i, v),只取存在边(v, w)的点,来将时间复杂度降低到O(m·n)。