网络流:最小费用最大流(最简单的算法)

合集下载

最小费用最大流

最小费用最大流
最小费用最大流
Spfa实现
概念
• 网络流图论中的一种理论与方法,研究网络 上的一类最优化问题 。 • 所谓网络或容量网络指的是一个连通的赋权 有向图 D=(V、E、C) , 其中V 是该图的 顶点集,E是有向边(即弧)集,C是弧上的容 量。此外顶点集中包括一个起点和一个终点。 网络上的流就是由起点流向终点的可行流, 这是定义在网络上的非负函数,它一方面受 到容量的限制,另一方面除去起点和终点以 外,在所有中途点要求保持流入量和流出量 是平衡的。
(3,3,1)
v0
(2,2,1) v3
(1,1,1)
(4,6,0)
v2
(2,3,0) (2,2,0)
(9,3,0) v5
(3,4,0)
如果 f 是可行流,则对收、发点vt、vs有
∑fsi =∑fjt =Wf ,
即从vs点发出的物质总量 = vt点输入的量.Wf 称为网络流 f 的总流量.
上述概念可以这样来理解,如G是一个运输网络,则 发点vs表示发送站,收点vt表示接收站,中间点vk表示中间 转运站,可行流 fij 表示某条运输线上通过的运输量,容量 Cij表示某条运输线能承担的最大运输量,Wf 表示运输总 量.
将各弧的单位运费作为长度,求v0到vn的最短 增流路v0v1v3v4vn,路长为8,可增加1单位的 流值。
v1
(4,3,0)
v4 (2,5,0) vn
(3,3,0)
v0
(2,2,0) v3
(1,1,0)
(4,6,0)
v2
(2,3,0) (2,2,0)
(9,3,0) v5
(3,4,0)
将各弧的单位运费作为长度,求v0到vn的最短 增流路v0v1v3v4vn,路长为8,可增加1单位的 流值。

网络流:最小费用最大流(最简单的算法)

网络流:最小费用最大流(最简单的算法)

网络流:最小费用最大流(最简单的算法)最小费用流在OI 竞赛中应当算是比较偏门的内容,但是NOI2008 中employee 的突然出现确实让许多人包括zkw 自己措手不及。

可怜的zkw 当时想出了最小费用流模型,可是他从来没有实现过,所以不敢写,此题0 分。

zkw 现在对费用流的心得是:虽然理论上难,但是写一个能AC 题的费用流还算简单。

先贴一个我写的employee 程序:只有不到70 行,费用流比最大流还好写~程序代码:C++#include <cstdio>#include <cstring>using namespace std;const int maxint=~0U>>1;int n,m,pi[550]={0},cost=0;bool v[550]={0};struct etype{int t,c,u;etype *next,*pair;etype(){}etype(int t_,int c_,int u_,etype* next_):t(t_),c(c_),u(u_),next(next_){}void* operator new(unsigned,void* p){return p;}} *e[550],*eb[550];int aug(int no,int m){if(no==n)return cost+=pi[1]*m,m;v[no]=true;for(etype *&i=e[no];i;i=i->next)if(i->u && !v[i->t] && pi[i->t]+i->c==pi[no])if(int d=aug(i->t,m<i->u?m:i->u))return i->u-=d,i->pair->u+=d,d;return 0;}bool modlabel(){int d=maxint,c;for(int i=1;i<=n;++i)if(v[i])for(etype *j=eb[i];j;j=j->next)if(j->u && !v[j->t])if((c=j->c-pi[i]+pi[j->t])<d)d=c;if(d==maxint)return false;for(int i=1;i<=n;++i)if(v[i])pi[i]+=d,e[i]=eb[i];return true;}int main(){freopen("costflow.in","r",stdin);freopen("costflow.out","w",stdout);scanf("%d %d",&n,&m);etype *Pe=new etype[m+m];while(m--){int s,t,c,u;scanf("%d%d%d%d",&s,&t,&u,&c);e[s]=new(Pe++)etype(t, c,u,e[s]);e[t]=new(Pe++)etype(s,-c,0,e[t]);e[s]->pair=e[t];e[t]->pair=e[s];}memmove(eb,e,sizeof(e));do do memset(v,0,sizeof(v));while(aug(1,maxint));while(modlabel());printf("%d\n",cost);return 0;}程序代码:CB大牛翻译的PASCALvarn,m,i,l,s,t,c,cost,u:longint;v:array[0..600]of boolean;dis:array[0..600]of longint;e_n,e_t,e_c,e_u,e_p,e_x:array[0..250000]of longint;function min(a,b:longint):longint;beginif a>b then exit(b);exit(a);end;procedure addedge(s,t,c,u,k:longint);begininc(l);e_n[l]:=e_n[s];e_n[s]:=l;//下一条边e_t[l]:=t;//边的另一端e_c[l]:=c;//边的费用e_u[l]:=u;//边的容量e_p[l]:=l+k;//对应的边end;procedure build(s,t,c,u:longint);beginaddedge(s,t,c,u,1);addedge(t,s,-c,0,-1);end;function aug(no,m:longint):longint;vari,d:longint;beginif no=n then begininc(cost,m*dis[1]);exit(m);end;v[no]:=true;i:=e_x[no];while i<>0 do beginif (e_u[i]>0)and(not v[e_t[i]])and(dis[e_t[i]]+e_c[i]=dis[no]) then begind:=aug(e_t[i],min(m,e_u[i]));if d>0 then begindec(e_u[i],d);inc(e_u[e_p[i]],d);e_x[no]:=i;exit(d);end;end;i:=e_n[i];end;e_x[no]:=i;exit(0);end;function modlabel:boolean;vard,i,j:longint;begind:=maxlongint;for i:=1 to n do if v[i] then beginj:=e_n[i];while j<>0 do beginif (e_u[j]>0)and(not v[e_t[j]])and(e_c[j]-dis[i]+dis[e_t[j]]<d) then d:=e_c[j]-dis[i]+dis[e_t[j]];j:=e_n[j];end;end;if d=maxlongint then exit(true);for i:=1 to n do if v[i] then beginv[i]:=false;inc(dis[i],d);end;exit(false);end;beginassign(input,'coflow.in');reset(input);assign(output,'coflow.out');rewrite(output);readln(n,m);l:=n;for m:=m downto 1 do beginreadln(s,t,u,c);build(s,t,c,u);end;repeatfor i:=1 to n do e_x[i]:=e_n[i];while aug(1,maxlongint)>0 do fillchar(v,sizeof(v),0);until modlabel;writeln(cost);close(output);end.这里使用的是连续最短路算法。

网络流

网络流

| f |
2 4/10 4/10
0/9
5 0/10 0/15
0/15
4/8 3 0/4 0/ 6 4 7 0/30 6 0/15
0/5
s
4/10
t 0/10
0/15
流量 容量
|f|=4
|f|为可行流f的流量
11
最大流问题
• 最大流问题 给一流网络G,源 s以及汇t。求一 个网络的流{f(i,j)},使其流量|f|达到最大 且是可行流。
0/10
v1 1/4 4/9
v3
15/20
t 4/4
7/7
路径P:{s,v1,v3,v4,t}中,边(v3,v4)是后向 边,其余为前向边。 后向边(V3,V4)的可改进量是7 前向边(s,v1)的可改进量是16-11=5
16

可改进路(增广路, Augmenting Path)
对于源s到汇t的一条简单路,如果路上的每 条边(i,j)的可改进量均大于0,则称这条路为一条 可改进路(增广路)。
所有边的可改进量的最小值为可改进路的 改进量
17
图(a)(b) 的可改进路P(V1,V3,V2,V4,V6).
18
• 调整该路径的流量 确定改进量a: 先看P+={(V1,V3),(V2,V4),(V4,V6)} c(1,3)-f(1,3)=8-2=6 c(2,4)-f(2,4)=4-0=4 c(4,6)-f(4,6)=7-2=5 再看P的后向边集合p-={(V3,V2)}, f(3,2)=2 因此改进量a至多取2。使改进后的前向边流量增 加,又使改进后的后向边的流量不为负。 • 改进后流量增为2。
42 42
最大流问题的推广
二、点有容量限制的情形

最小费用最大流问题.

最小费用最大流问题.
(
vs
(
5,2)
(
(
2,6)
8,1)
V2 10,3)ቤተ መጻሕፍቲ ባይዱV3
4,2)
第一轮:f 0为初始可行流,作相应的费用有向图网络L(f 0),如 图(a)。 在L(f 0)上用DijksTra标号法求出由vs到vt的最短路(最小费用链) 0 m i n 8,5, 5 7 μ0=(vs,v2,v1, ( vt)v ,并对 μ 按 进行流量的调整, 0 , v ) ,( v , v ) ,( v , v ) s 2 0 2 1 0 1 t 0 由于, (1) (1) 所以有 fs2 f12 f1t(1) 5,其余不变,得新的可行流f1的流量 有向图(b)。
vs
vt
2.下表给出某运输问题的产销平衡表与单位运价 表。将此问题转化为最小费用最大流问题,画出网 络图并求数值解。 2 3 产量 1 产地 销地
A B 销量 20 30 4 24 22 5 5 20 6 8 7
最小总费用为240
(20,8) A (0,8) s (30,7) (0,7) (5,8) (24,8)
4
vt
vs
1
6
2
2
v1
(7,5)
(2,0)
(10,0)
vt
(4,0)
v2
V(f
1)
(a) = 5
3
v3 vs
(8,5)
w(f0)
(5,5)
v2
(10,0)
v3
(b) f 1
v1 vs
(8,5)
(7,5)
(2,0)
(10,0)
vt
(4,0) 4
v1
vs

最小费用最大流问题

最小费用最大流问题

近似算法和启发式算法
要点一
近似算法
近似算法是一种用于求解NP-hard问题的有效方法,它可 以在多项式时间内找到一个近似最优解。最小费用最大流 问题的近似算法包括Ford-Fulkerson算法、EdmondsKarp算法等。
要点二
启发式算法
启发式算法是一种基于经验或直观的算法,它可以在合理 的时间内找到一个近似最优解。最小费用最大流问题的启 发式算法包括基于增广路径的算法、基于贪婪的算法等。
研究如何将最小费用最大流问题 应用于计算机科学领域,例如计 算机网络、云计算等。
物理学
研究如何借鉴物理学中的理论和 思想,解决最小费用最大流问题, 例如利用流体动力学中的思想来 研究网络中的流。
谢谢观看
Hale Waihona Puke 06未来研究方向和展望算法优化和改进
动态规划算法
研究如何优化动态规划算法,减少时间复杂度 和空间复杂度,提高求解效率。
近似算法
研究近似算法,在保证求解质量的前提下,提 高求解速度。
并行计算和分布式计算
研究如何利用并行计算和分布式计算技术,加速最小费用最大流问题的求解。
新的问题定义和模型
考虑更复杂的情况
和技术。
有界容量和无界容量
总结词
有界容量和无界容量是指在网络中节点之间 的容量是否有限制。
详细描述
在最小费用最大流问题中,如果节点之间的 容量有限制,即为有界容量问题;如果节点 之间的容量没有限制,即为无界容量问题。 有界容量问题可以通过增广路径算法、预流 推进算法等求解,而无界容量问题则需要采
用其他算法和技术进行求解。
算法概述
最小费用最大流问题是一种网络流问 题,旨在在给定有向图中寻找一条路 径,使得从源节点到汇点之间的总流 量最大,同时满足每个节点的流入量 等于流出量,以及每条边的容量限制。

最小费用最大流问题

最小费用最大流问题

i):f(j,i))=0; ); @sum(edge(i,j)|i#eq#@index(s):f(i,j)) =vf; @sum(edge(j,i)|i#eq#@index(t):f(j,i)) =vf; @for(edge(i,j):@bnd(0,f(i,j),u(i,j))) ; end

min
( i , j )E

cij fij ;
s.t.
jV ( i , j )E

fij
jV ( j ,i )E

v f , i s , f ji v f , i t , 0, i s, t.
0 fij uij ,(i, j ) E.
LINGO 程序求解 model: sets: points/s,v1,v2,v3,v4,t/; edge(points,points) /s,v1 s,v2 v1,v2 v1,v3 v2,v4 v3,v2 v3,t v4,v3 v4,t/:c,u,f; endsets data: c=2 8 5 2 3 1 6 4 7; u=8 7 5 9 9 2 5 6 10; vf=14; enddata min=@sum(edge(i,j):c(i,j)*f(i,j)); @for(points(i)|i#ne#@index(s) #and# i#ne#@index(t): @sum(edge(i,j):f(i,j))-@sum(edge(j,
最小费用最大流问题
例 本例是最大流问题的延伸,由于输油管道的长短不 一,或地质等原因,使每条管道上运输费用也不相 同,因此,除考虑输油管道的最大流外,还需要考 虑输油管道输送最大流的最小费用,下图所示是带 有运输费的网络,其中第 1 个数字是网络的容量, 第 2 个数字是网络的单位运费.

最小费用最大流简介

最小费用最大流简介

6
最大流=f1+f2+f3=4+2+2=8
最小费用=48+26+30=104
算法设计:贪心策略
设p是图的一条增广路径,定义路径p的长度为:

w[i, j ]

w[i, j ]
i , j P
i ,。
如果p是一条最短(单位费用最小)的可增广路径, 称p是一条最小费用可增广路。
(4,6)
实例:
(容量,单位费用)
(2,5)
2
(5,7)
3
(4,3)
1
(6,2)
6 5
(8,5)
4
(7,6)
①、最小费用可增广路(最短路径) 1436 长度(单位流量总费用) =2+7+3=12 f 1=4 cost1=4*12=48
(4,6) (2,5)
2
4
(5,7) 4
3
4
(4,3)
1
(6,2)
6 5
(8,5)
4
(7,6)
(4,6)
(2,5)
2
4 (5,7) 4
3
4
(4,3)
1
②、最小费用可增广路 1456 长度(单位流量总费用) =2+6+5=13 f2 =2 cost2=2*13=26
6 5
(8,5)
(6,2)
4
(7,6)
(4,6)
(2,5)
2
(5,7) 2 4 2 (7,6)
// short[i]:i到源点1的最短距离(最小费用);
b:array[1..maxn] of integer; // b[i]:最小费用可增广路 径上结点i的前驱

最小费用最大流

最小费用最大流

vs
4
v2
4
vt
(10 )f ( 5)
v1 1
v3
4 -4
-1
3 2
2 -6
vs
-1
v2
6
vt
(11) L( f ( 5))
运筹学
的增广链u,以1调整f,得到新的可行流
f′时,b(f′)比b(f)增加多少?
b
u i j
显然有
b(f')﹣b(f)=[ b
b b (f′ij﹣fij )]u
ij
bu(_ f′i j ij﹣fij )﹣
b b
= [ ﹣ u i j
u_ i j
]
我们把[ u
﹣ i j
u_
ij
] 称为这条增广链u的
费用。
v2 (6 ,7)
vt vs
1
v2
6
vt
v1
3
v3 1=3
W(f(1))=3
(1) L(f (0))
v1 1
v3
0
3
-1
-2
3
0
4 -2
2 3
vs
3
v2
0
(2) f ( 1)
-1
vt
vs
1
v2
6
vt
(3) L(f (1))
v1 (1 ,6) v3
(4 ,8) (2 ,3)
(2 ,5) (3 ,2)
1
v3
v1
4
v3
4=3 W(f(4))=8
4
-1
-2 3
-2 -3
4 0
5 1
vs
-1
v2
6
(7) L(f (3))

说说网络流与最小费用问题

说说网络流与最小费用问题

说说网络流与最小费用问题网络流与最小费用问题是图论中的一个重要研究方向,涉及网络流量的分配和优化问题。

本文将对网络流与最小费用问题进行详细的介绍和分析。

一、网络流网络流指的是在一个有向图中,每条边都有一个容量限制,通过这个网络,从源节点向汇节点发送流量。

在网络流问题中,我们需要找到一种最优的方法来分配流量,使得流量能够有效传输,并且满足各个节点的需求和容量限制。

二、最小费用问题最小费用问题是在网络流问题的基础上引入一种费用的考虑,即给每条边赋予一个权重,表示通过该边传输流量的成本。

最小费用问题旨在找到一种最优的流量分配方案,使得总花费最小化。

三、网络流与最小费用问题的关系网络流与最小费用问题密切相关,实际上最小费用问题可以看作是网络流问题的一个特例。

在最小费用问题中,我们通过引入费用的考虑,进一步优化流量的分配方案,使得在满足容量限制的同时,总花费最少。

四、网络流与最小费用问题的求解方法1. 网络流问题的求解方法:- 最大流问题:通过寻找一个从源节点到汇节点的路径,使得路径上的流量最大化,从而达到最大化整个网络中的流量。

- 最小割问题:寻找一个割集,将源节点和汇节点分割开,使得被割掉的边的容量之和最小。

最小割问题与最大流问题是互补的关系。

- 最小费用流问题:在网络流问题的基础上引入费用的思考,目标是找到一个流量分配方案,使得总花费最小化。

2. 最小费用问题的求解方法:- 费用流算法:费用流算法是解决最小费用问题的一种常用方法,主要包括增广路算法和最短路算法。

- 增广路算法:增广路算法是一种通过不断寻找增广路来优化流量分配方案的方法。

增广路是指一条从源节点到汇节点的路径,通过增加路径上的流量,可以降低总花费。

- 最短路算法:最短路算法是一种基于图的最短路径问题求解思想的方法。

通过在网络中寻找最短的路径来优化流量分配方案,并不断更新路径上的费用。

五、网络流与最小费用问题的应用网络流与最小费用问题有着广泛的应用场景,包括但不限于以下几个方面:1. 运输规划:在城市道路或物流配送中,通过网络流问题的求解,可以实现货物的最优分配和运输路径规划,从而提高运输效率和节约成本。

运筹学第六章6.5最小费用最大流问题

运筹学第六章6.5最小费用最大流问题
该算法基于Ford-Fulkerson方法和增广路径的概念,通过不断寻找增广路径并更 新流,最终得到最大流。
预处理步骤
初始化
为每个节点和边设置相应的容量和费 用。
残量网络构建
寻找增广路径
在残量网络中寻找增广路径,即从源 点到汇点存在一条路径,该路径上的 所有边都未满载且具有正的残量。
根据边的容量和费用,构建残量网络。
05
算法的复杂度和优化
时间复杂度分析
算法时间复杂度
最小费用最大流问题通常使用Ford-Fulkerson算法或其变种来解决,时间复杂度为O(V^3 * E),其中V是 顶点数,E是边数。
优化策略
为了提高算法效率,可以采用预处理、动态规划、记忆化搜索等策略,减少不必要的计算和重复计算 。
空间复杂度分析
最小费用最大流问题可以应用于多种 实际场景,如物流运输、能源分配、 通信网络等。
背景和重要性
最小费用最大流问题作为网络流问题 的一个重要分支,在计算机科学、运 筹学和工程领域具有广泛的应用价值。
解决最小费用最大流问题有助于优化 资源配置、降低成本和提高效率,对 于实际问题的解决具有重要的意义。
02
此外,随着计算科学和数据科学的快速发展,如 何利用新的技术和方法来求解最小费用最大流问 题也是值得关注的方向。
例如,如何设计更高效的算法来求解大规模的最 小费用最大流问题?如何处理具有特殊性质的最 小费用最大流问题?如何将最小费用最大流问题 的思想和方法应用到其他领域?
因此,未来对于最小费用最大流问题的研究仍具 有广阔的空间和挑战性。
案例一:简单网络流问题
问题描述
给定一个有向图G(V,E),其中V是顶点的集合, E是边的集合。每条边(u,v)有一个非负的容量 c(u,v)和一个非负的费用f(u,v)。求从源点s到 汇点t的最大流,使得流的总费用最小。

最优网络流问题的近似算法

最优网络流问题的近似算法

最优网络流问题的近似算法最优网络流问题是一类经典的组合优化问题,它在许多实际应用中具有广泛的应用,如交通调度、电力分配等。

然而,由于该问题的复杂性,求解最优网络流问题在实际中往往是困难的。

因此,研究者提出了各种近似算法来解决这一问题。

本文将介绍几种常用的最优网络流问题的近似算法,并对其性能进行评估。

一、最小费用最大流算法最小费用最大流算法是求解最优网络流问题的一种有效方法。

该算法通过在原网络中构造一个对偶网络,并利用最短路径算法来求解最大流,从而得到最小费用最大流。

该算法的时间复杂度为O(EFlogV),其中E和F分别是网络中的边数和流量。

然而,最小费用最大流算法的精确性较高,但在大规模网络中的计算复杂度较高,因此无法满足实际应用的需求。

为了解决这一问题,研究者们提出了一种近似算法。

二、近似最优网络流算法1. 贪心算法贪心算法是求解最优网络流问题的一种简单而常用的近似算法。

该算法从源点开始,每次选择一条容量大于零且费用最小的边,并流过该边的最大流量。

然后,更新网络流量和费用,并继续选择下一条边,直到流达终点或无法再增加流量为止。

尽管贪心算法的思想简单,但其精度较低。

对于一些特殊情况,贪心算法可能会得到次优解甚至是不可行解。

因此,在实际应用中,贪心算法往往不适用于求解精确的最优网络流问题,但可以用于快速求解近似解。

2. 近似最小费用最大流算法近似最小费用最大流算法是在最小费用最大流算法基础上进行改进的一种近似算法。

该算法通过引入松弛因子来平衡费用和流量之间的关系,使得在每次迭代中可以选择一些费用相对较高但最大流量较大的边进行更新。

通过不断迭代和调整松弛因子的值,近似最小费用最大流算法可以逐渐接近最优解。

不过,近似最小费用最大流算法的计算复杂度较高,而且在一些较为复杂的网络中可能无法获取精确解。

因此,在实际应用中,该算法往往被用作一种启发式算法,通过近似的方式来求解最优网络流问题。

三、性能评估针对最优网络流问题的近似算法,其性能往往会受到算法本身和问题实例的影响。

图论专题小结:最小费用最大流算法

图论专题小结:最小费用最大流算法

图论专题小结:最小费用最大流算法一,给定流量F,求最小费用题意:网络中有两台计算机s,t。

现在每秒钟要从s到t传输大小为F的数据到t。

该网络中一共有N台计算机,其中有一些靠单向电缆相连接每条电缆用(from,to,cap,cost)表示从from发送给to,最大容量是cap,单位传输费用是cost。

问传输数据最小的花费是多少?解决最小费用流的一般思路是:每次都沿着最短路进行增广,增广一次之后累加本次增广的总费用,同时修改剩余的流量F,当F≤0时或dist[t]==INF时退出。

利用改进的Dijkstra算法求解(1)概述:题目要求在存在流量为F的前提下,总花费最少。

这类问题就是最小费用流问题。

该问题可以采用加入“势函数”后的Dijkstra算法解决。

因为对于每条边e=(u,v),有如下事实成立:h(v)≤h(u)+e.cost(其中h[u]表示s到u的最短距离)。

因此令dist[v]=dist[u]+e.cost+h[u]-h[v],。

那么所有的dist值必然大于等于0,这样就能用Dijkstra算法求解了。

下面代码中用了一个优先队列,每次优先出列dist值小的元素。

整个算法的时间复杂度是O(F*ElogV)(F是流量,E是边数,V是顶点数)。

1.#include<iostream>2.#include<algorithm>3.#include<string>4.#include<sstream>5.#include<set>6.#include<vector>7.#include<stack>8.#include<map>9.#include<queue>10.#include<deque>11.#include<cstdlib>12.#include<cstdio>13.#include<cstring>14.#include<cmath>15.#include<ctime>16.#include<functional>ing namespace std;18.19.#define N 100020.#define INF 10000000021.typedef pair<int, int>P;//first保存最短距离,second保存顶点的编号22.23.struct Edge24.{25.int to, cap, cost, rev;//终点,容量(指残量网络中的),费用,反向边编号26.Edge(int t, int c, int cc, int r) :to(t), cap(c), cost(cc), rev(r){}27.};28.int V;//顶点数29.vector<Edge>G[N];//图的邻接表30.int h[N];//顶点的势31.int dist[N];//最短距离32.int prevv[N];//最短路中的父结点33.int preve[N];//最短路中的父边34.35.void addedge(int from, int to, int cap, int cost)36.{37.G[from].push_back(Edge( to, cap, cost,G[to].size()));38.G[to].push_back(Edge( from, 0, -cost, G[from].size() - 1 ));39.}40.int min_cost_flow(int s, int t, int f)//返回最小费用41.{42.int res = 0;43.fill(h, h + V, 0);44.while (f>0)//f>0时还需要继续增广45.{46.priority_queue<P, vector<P>, greater<P> >q;47.fill(dist, dist + V, INF);//距离初始化为INF48.dist[s] = 0;49.q.push(P(0, s));50.while (!q.empty())51.{52.P p = q.top(); q.pop();53.int v = p.second;54.if (dist[v]<p.first)continue;//p.first是v入队列时候的值,dist[v]是目前的值,如果目前的更优,扔掉旧值55.for (int i = 0; i<G[v].size(); i++)56.{57.Edge&e = G[v][i];58.if (e.cap>0 && dist[e.to]>dist[v] + e.cost + h[v] - h[e.to])//松弛操作59.{60.dist[e.to] = dist[v] + e.cost + h[v] - h[e.to];61.prevv[e.to] = v;//更新父结点62.preve[e.to] = i;//更新父边编号63.q.push(P(dist[e.to], e.to));64.}65.}66.}67.if (dist[t] == INF)//如果dist[t]还是初始时候的INF,那么说明s-t不连通,不能再增广了68.return -1;69.for (int j = 0; j<V; j++)//更新h70.h[j] += dist[j];71.int d = f;72.for (int x = t; x != s; x = prevv[x])73. d = min(d, G[prevv[x]][preve[x]].cap);//从t出发沿着最短路返回s找可改进量74. f -= d;75.res += d*h[t];//h[t]表示最短距离的同时,也代表了这条最短路上的费用之和,乘以流量d即可得到本次增广所需的费用76.for (int x = t; x != s; x = prevv[x])77.{78.Edge&e = G[prevv[x]][preve[x]];79. e.cap -= d;//修改残量值80.G[x][e.rev].cap += d;81.}82.}83.return res;84.}85.86.int main()87.{88.freopen("t.txt", "r", stdin);89.int m;90.while (cin >> V >> m)91.{92.for (int i = 0; i<m; i++)93.{94.int from, to, cap, cost;95.cin >> from >> to >> cap >> cost;96.addedge(from, to, cap, cost);97.}98.int s, t, f;99.cin >> s >> t >> f;100.cout << min_cost_flow(s, t, f) << endl;101.}102.return 0;103.}104.二,网络输出最大流时,求出最小的费用这就是最小费用最大流问题:既要求出最大流,又要求出达到最大流时候的最小费用。

网络流--费用流Ek算法讲解

网络流--费用流Ek算法讲解

⽹络流--费⽤流Ek算法讲解前⼀篇博客写的是最⼤流。

先简要说⼀下思路,⽅便下⾯的讲解。

最⼤流求解是先跑⼀遍bfs,把每个点定义⼀个深度,跑dfs的同时连接⼀条反向边⽅便反悔,避免不必要的时间。

现在说⼀下费⽤流。

费⽤流的全称是最⼩费⽤最⼤流(或最⼤费⽤最⼤流),保证最⼩费⽤的情况下跑最⼤流。

最⼩费⽤?BFS -> SPFA成功解决。

我们不需要再给每个点定义深度,⽽是直接把费⽤当成边权求⼀遍到原点的最短路。

但是,流量的反向边初始值为0,费⽤是0吗?显然不是,因为如果不需要⾛这条边显然是不需要费⽤的,所以正向边+反向边的费⽤为0.显然费⽤为-cost。

下⼀个问题,如果我们找到了最短路,如何寻找路径把费⽤累加并更改流量?SPFA解决!之后我们当前dfs的流量需要⽤⼀个数组存起来,⽽不是⼀个变量。

之后就没什么了,不会的问我。

题⽬描述如题,给出⼀个⽹络图,以及其源点和汇点,每条边已知其最⼤流量和单位流量费⽤,求出其⽹络最⼤流和在最⼤流情况下的最⼩费⽤。

输⼊格式:第⼀⾏包含四个正整数N、M、S、T,分别表⽰点的个数、有向边的个数、源点序号、汇点序号。

接下来M⾏每⾏包含四个正整数ui、vi、wi、fi,表⽰第i条有向边从ui出发,到达vi,边权为wi(即该边最⼤流量为wi),单位流量的费⽤为fi。

输出格式:⼀⾏,包含两个整数,依次为最⼤流量和在最⼤流量情况下的最⼩费⽤。

样例输⼊:4 5 4 34 2 30 24 3 20 32 3 20 12 1 30 91 3 40 5样例输出:50 280code:#include<cstdio>#include<algorithm>#include<queue>#include<cstring>using namespace std;#define N 100000int n,m,S,T;int f[N];int now[N];int head[N];int to[N];int val[N];int nex[N];int cost[N];int idx=1;int inq[N];int pre[N];int maxflow;int ans;int a,b,c,d;int nowflow[N];void addedge(int a,int b,int c,int d){nex[++idx]=head[a];head[a]=idx;to[idx]=b;val[idx]=c;cost[idx]=d;}bool spfa(int S,int T){queue<int > q;memset(f,0x3f,sizeof(f));memset(inq,0,sizeof(inq));memset(nowflow,0x3f,sizeof(nowflow));q.push(S);f[S]=0;inq[S]=1;//cost[S]=1<<30;while(!q.empty()){int x=q.front();inq[x]=0;q.pop();for(int i=head[x];i;i=nex[i]){if(val[i]>0&&f[to[i]]>f[x]+cost[i]){f[to[i]]=f[x]+cost[i];nowflow[to[i]]=min(nowflow[x],val[i]); pre[to[i]]=i;if(!inq[to[i]]){inq[to[i]]=1;q.push(to[i]);}}}}if(f[T]>=0x3f3f3f3f)return 0;return 1;}void EK(){int x=T;while(x!=S){int i=pre[x];val[i]-=nowflow[T];val[i^1]+=nowflow[T];x=to[i^1];}maxflow+=nowflow[T];ans+=f[T]*nowflow[T];}int main(){scanf("%d%d%d%d",&n,&m,&S,&T);for(int i=1;i<=m;i++){scanf("%d%d%d%d",&a,&b,&c,&d);addedge(a,b,c,d);addedge(b,a,0,-d);}while(spfa(S,T))EK();printf("%d %d",maxflow,ans);}。

网络系统的最小费用最大流问题

网络系统的最小费用最大流问题
净输入量)
网络系统的最大流问题
• 任意一个网络上的可行流总是存在的。例如零流v(f )=0,
就是满足以上条件的可行流。 • 网络系统中最大流问题就是在给定的网络上寻求一个可行
流f ,使其流量v(f )达到最大值。 • 设流f={fij}是网络D上的一个可行流,我们把D中fij=cij的
弧叫做饱和弧,fij<cij的弧叫做非饱和弧,fij>0的弧为非 零流弧,fij=0的弧叫做零流弧。
查点。(考虑后向弧)
于是vi成为标号已检查的点。重复以上步骤,如果所有的标
号都已经检查过,而标号过程无法进行下去,则标号法结束。这时
的可行流就是最大流。但是,如果vt被标上号,表示得到一条增广 链μ,转入下一步调整过程。
网络系统的最大流问题
2. 调整过程
首先按照vt和其他点的第一个标号,利用“反向追踪” 的办法,找出增广链 。例如,令vt的第一个标号是vk,则 弧(vk,vt)在μ上。再看vk的第一个标号,若是vi,则弧 (vi,vk)都在μ上。依次类推,直到vs为止。这时,所找出 的弧就成为网络D的一条增广链μ。取调整量θ= l(vt),即 vt的第二个标号,
未检查点vi,对一切未标号点vj:
网络系统的最大流问题
1) 如 果 在 弧 ( vi ,vj ) 上 , fij<cij , 那 么 给 vj 标 号 (vi ,l(vj)),其中l(vj)=min[l(vi),cij -fij].这时,vj 成
为标号未检查的点。(考虑前向弧)
2) 如 果 在 弧 ( vj ,vi ) 上 , fji>0, 那 么 给 vj 标 号 ( -vi , l(vj)),其中l(vj)=min[l(vi),fji].这时,vj成为标号未检

最大网络流最小费用流模型

最大网络流最小费用流模型


该函数满足:

(1)对于G的残流网络中的每一条边(u,v)有,h(u) h(v)+1; (2)h(t)=0。 G的残流网络中满足h(u) = h(v)+1的边(u,v)称为G的可推流边。
11
一般的预流推进算法
步骤0:构造初始预流flow: 对源顶点s的每条出边(s,v)令flow(s,v)=cap(s,v); 对其余边(u,v)令flow(u,v)=0。构造一有效的高度函数h。 步骤1:如果残量网络中不存在活顶点,则计算结束,已经得到最大流; 否则转步骤2。 步骤2:在网络中选取活顶点v。 如果存在顶点v的出边为可推流边,则选取一条这样的可推流边,并沿此边推流。 否则令h(v) = min{h(w)+1 | (v,w)是当前残流网络中的边},并转步骤1。
6



2 算法描述 最大流的增广路算法如下。该算法也常称作Ford Fulkerson算法。
template <class Graph, class Edge> class MAXFLOW { const Graph &G; int s, t,maxf; vector<int> wt; vector<Edge *> st; int ST(int v) const { return st[v]->other(v); } void augment(int s, int t) { int d = st[t]->capRto(t); for (int v = ST(t); v != s; v = ST(v)) if (st[v]->capRto(v) < d) d = st[v]->capRto(v); st[t]->addflowRto(t, d); maxf+=d; for ( v = ST(t); v != s; v = ST(v)) st[v]->addflowRto(v, d); } bool pfs(); public: MAXFLOW(const Graph &G, int s, int t,int &maxflow) : G(G), s(s), t(t), st(G.V()), wt(G.V()),maxf(0) { while (pfs()) augment(s, t); maxflow+=maxf;} };

最小费用最大流:二分答案(bushi)

最小费用最大流:二分答案(bushi)

最⼩费⽤最⼤流:⼆分答案(bushi)最⼩费⽤最⼤流问题前置知识关于⽹络最⼤流, 这是本博客需要⽤到的两个算法:另外还有效率更⾼, 可是最⼩费⽤最⼤流不会⽤到的两个算法:前置知识就只有第⼀篇博客的内容, 需要提前学习完再来看费⽤流.简介最⼩费⽤最⼤流问题, 简称费⽤流问题, Wikipedia 中的名称是: "Minimum-cost flow problem", 缩写为 "MCFP".问题给出⼀个⽹络, 每条边除了有容量属性外, 还有⼀个属性: 单位流量费⽤. 也就是说, ⼀条边的费⽤是流经这条边的流量和这条边单位流量费⽤的乘积. 要求给出保证最⼤流的情况下的最⼩费⽤.EK·改EK 算法就是每次⽤ BFS 在残量⽹络上找到⼀条可以增⼴的路并且增⼴它. 为了防⽌反复横跳, 我们使⽤ BFS ⽽不是 DFS 寻找增⼴路, 单条增⼴路寻找复杂度O(n+m).所以保证最⼤流, 不需要考虑增⼴路是怎么找的, 只要找到就可以, 所以可以使⽤最短路算法, 每次以S为起点, 以单位花费为边权, 每次求S 到T的最短路, 这个最短路长度乘上这条增⼴路的流量计⼊答案, 然后进⾏下⼀轮的增⼴.算法选择⽅⾯, 因为⼀条边的回边的边权是它的边权的相反数, 所以残量⽹络存在负权, 对于有负权的图的最短路, 我们使⽤ SPFA 算法.Dinic·改Dinic ⼀次求多条增⼴路, ⽽ SPFA 也是⼀次求S到所有点的最短路, 所以两者也可以结合.具体操作是每次 BFS 分层的过程改为求S的单源最短路的过程. 在 DFS 时的规则也有所改变, 传统的 Dinic 中, 我们只允许流从⼀个点, 流向深度⽐它⼤ 1 的点, 是为了保证流不会反复横跳 (我们设定的⼀切规则都是为了规范流的⽅向⽽避免反复横跳, 从算法⽅⾯印证了规则之于⾃由的重要性). 在改进版算法中, 我们使⽤最短路来规范流的流动, 只要保证⼀条边是S到这条边的终点的最短路的⼀部分, 这条路就能⾛.这个规则既保证了我们的选择⼀定是最短路, ⼜保证了流的有序性, ⽽且不破坏 Dinic 的特性: 多路增⼴.实现起来也可以说⽐较写意, 但是需要注意的是, DFS 的过程和⼀般的 Dinic 相⽐增加了⼀个InQue标记.这是因为最短路为序相⽐深度为序来说, 边权对顺序是有影响的, 也就是说当⼀条边和它的回边都有流量的时候, 它们构成⼀个零环, 意思是流量在这两点之间⽆论⾛⼏个来回, 花费都不变.为了避免这种情况, 我们像 SPFA ⼀样使⽤InQue标记, 防⽌增⼴路径上⼀个点出现两次.unsigned m, n, Hd, Tl;int C, D, Flow(0), Cost(0), Tmp(0);struct Node;struct Edge {Node *To;Edge *Nxt;int Value, Contain;}E[100005], *CntE(E - 1);struct Node {Edge *Fst;int Dist;char InQue;}N[5005], *S, *T, *A, *B, *Q[5005];char SPFA() {Tl = Hd = 0;for (register unsigned i(1); i <= n; ++i) N[i].Dist = 0x3f3f3f3f;Q[++Tl] = S;S->Dist = 0;S->InQue = 1;register Node *x;while (Hd ^ Tl) {++Hd; if(Hd > 5000) Hd -= 5000;x = Q[Hd];x->InQue = 0;register Edge *Sid(x->Fst);while (Sid) {if(Sid->Contain && Sid->To->Dist > x->Dist + Sid->Value) {Sid->To->Dist = x->Dist + Sid->Value;if(!(Sid->To->InQue)) {++Tl; if(Tl > 5000) Tl -= 5000;Q[Tl] = Sid->To;Sid->To->InQue = 1;}}Sid = Sid->Nxt;}}return (T->Dist < 0x3f3f3f3f);}int DFS(Node *x, int Come){if(x == T) return Come;x->InQue = 1;register Edge *Sid(x->Fst);register unsigned Go, Flew(0);while (Sid) {if(Sid->To->Dist == x->Dist + Sid->Value && Sid->Contain && (!(Sid->To->InQue))) {Go = DFS(Sid->To, min(Sid->Contain, Come));Sid->Contain -= Go;Come -= Go;E[(Sid - E) ^ 1].Contain += Go;Cost += Sid->Value * Go;Flew += Go;}if(!Come) break;Sid = Sid->Nxt;}x->InQue = 0;return Flew;};int main() {n = RD(), m = RD(), S = N + RD(), T = N + RD();for (register unsigned i(1); i <= m; ++i) {A = N + RD(),B = N + RD(),C = RDsg(),D = RDsg();(++CntE)->Nxt = A->Fst;A->Fst = CntE;CntE->Contain = C;CntE->Value = D;CntE->To = B;(++CntE)->Nxt = B->Fst;B->Fst = CntE;CntE->Value = -D;CntE->To = A;}while (SPFA()) Flow += DFS(S, 0x7fffffff);printf("%d %d\n", Flow, Cost);return Wild_Donkey;}改·改实际上叫做 "Primal-Dual Algorithm" (原始对偶算法).就像59·改和59关系不⼤⼀样, 这个算法虽然名字原始, 但是我⽆论如何也不明⽩它和对偶有什么关系, 如果让我为他取名, 我觉得应该叫做:队列优化的Bellman_Ford和Dijkstra最短路变化量累加求最⼩费⽤最⼤流联合算法由于 SPFA 的最坏复杂度可以达到O(nm), 所以相当于在原算法的基础上增加了⼀个因⼦m, 可以说毫⽆效率可⾔.所以考虑使⽤ Dijkstra, 但是图中有负权, Dijkstra 很难得到正确结果.算法效率低是因为运⾏了多次 SPFA, 但是如果我们只运⾏⼀次 SPFA, 算法效率也是不受影响的.⼀开始跑⼀遍 SPFA, 将S到每个点的最短路求出, 记为h, 我们称h x为点x的势.每次跑最短路, 发现因为残量⽹络越来越残, S到其它点的最短路只能变得更长, 不能变得更短.每次跑 Dijkstra 之前, 假设我们已经求出了每个点的势, 也就是这个残量⽹络变得这么残之前的最短路, 那么规定每条边 (a,b) 的权值|(a,b)|′=|(a,b)|+h a−h b.因为上⼀次 (a,b) 之间的最短路⼀定不会⽐ |(a,b)| 更长, 否则最短路就变成 |(a,b)| 了, 所以容易知道h b−h a≤|(a,b)|, 整理得|(a,b)|′=|(a,b)|+h a−h b≥0, 这样, 边权⼤于 0, 我们就能使⽤ Dijkstra 了.⼀条路径的权值和就变成了它们原来的权值之和加上起点的势再减去终点的势, 其意义是这条路径的起点终点之间的最短路在上次增⼴之后增长了多少. 也就是说, 我们⽤ Dijkstra 求的是⼀个增长量. 是最短路的导数所以从新的权值求得的最短路求真正的最短路的⽅法也变得明了了, 就是⽤⼀开始 SPFA 求的最短路加上每⼀次增⼴后 Dijkstra 求的最短路的总和, 就是当前真正的最短路.这样再拉去跑 Dinic/EK, 就和上⾯两个算法⼀样了, 但是⽹上的⽅法貌似都是⽤ EK, 但是理论上我们有了最短路, 就能像前⾯Dinic·改⼀样多路增⼴.于是我就把 Dinic + Dijkstra + SPFA 写出来了, 很遗憾, 它获得了⽐上⾯的程序更低的效率.unsigned m, n, Hd, Tl;int C, D, Flow(0), Cost(0), Tmp(0);struct Node;struct Edge {Node *To;Edge *Nxt;int Vnew, Value, Contain;}E[100005], *CntE(E - 1);struct Node {Edge *Fst;int Dist, h;char InQue;}N[5005], *S, *T, *A, *B, *Q[5005];struct Que {Node *P;inline const char operator<(const Que &x) const{return this->P->Dist > x.P->Dist;}}QTmp;priority_queue <Que> Qu;void SPFA() {Tl = Hd = 0;for (register unsigned i(1); i <= n; ++i) N[i].h = 0x3f3f3f3f;Q[++Tl] = S;S->h = 0;S->InQue = 1;register Node *x;while (Hd ^ Tl) {++Hd; if(Hd > 5000) Hd -= 5000;x = Q[Hd];x->InQue = 0;register Edge *Sid(x->Fst);while (Sid) {if(Sid->Contain && Sid->To->h > x->h + Sid->Value) {Sid->To->h = x->h + Sid->Value;if(!(Sid->To->InQue)) {++Tl; if(Tl > 5000) Tl -= 5000;Q[Tl] = Sid->To;Sid->To->InQue = 1;}}Sid = Sid->Nxt;}}}int DFS(Node *x, int Come){if(x == T) return Come;x->InQue = 1;register Edge *Sid(x->Fst);register unsigned Go, Flew(0);while (Sid) {if(Sid->To->h == x->h + Sid->Value && Sid->Contain && (!(Sid->To->InQue))) {Go = DFS(Sid->To, min(Sid->Contain, Come));Sid->Contain -= Go;Come -= Go;E[(Sid - E) ^ 1].Contain += Go;Cost += Sid->Value * Go;Flew += Go;}if(!Come) break;Sid = Sid->Nxt;}x->InQue = 0;return Flew;};char Dijkstra () {for (register unsigned i(1); i <= n; ++i) N[i].Dist = 0x3f3f3f3f;QTmp.P = S, Qu.push(QTmp);S->Dist = 0;S->InQue = 1;register Node *x;while (Qu.size()) {x = Qu.top().P;Qu.pop();x->InQue = 0;register Edge *Sid(x->Fst);while (Sid) {Sid->Vnew = Sid->Value + x->h - Sid->To->h;if(Sid->Contain && Sid->To->Dist > x->Dist + Sid->Vnew) { Sid->To->Dist = x->Dist + Sid->Vnew;if(!(Sid->To->InQue)) {QTmp.P = Sid->To;Qu.push(QTmp);Sid->To->InQue = 1;}}Sid = Sid->Nxt;}}for (register unsigned i(1); i <= n; ++i) N[i].h += N[i].Dist;return (T->h < 0x3f3f3f3f);}int main() {n = RD(), m = RD(), S = N + RD(), T = N + RD();for (register unsigned i(1); i <= m; ++i) {A = N + RD(),B = N + RD(),C = RDsg(),D = RDsg();(++CntE)->Nxt = A->Fst;A->Fst = CntE;CntE->Contain = C;CntE->Value = D;CntE->To = B;(++CntE)->Nxt = B->Fst;B->Fst = CntE;CntE->Value = -D;CntE->To = A;}SPFA();do {Flow += DFS(S, 0x7fffffff);} while(Dijkstra());printf("%d %d\n", Flow, Cost);return Wild_Donkey;}Processing math: 100%。

第6讲最大流最小费用

第6讲最大流最小费用
第六讲 最大流,最小费用
1. 网络、流、割 2. 最大流Ford-Fulkerson算法 3. 最大流最小费用问题 4. Busacker-Growan迭代算法
下 回

一、网络、流、割
网络N就是规定了源和汇,并且每条边都赋予 了非负整数权的赋权有向图D,其中此有向图D 称为网络N的基础有向图。 定义:若
这里所介绍的求最大流最小费用的算法是迭代 法,是由Busacker和Gowan在1961年提出的。 主要步骤如下:
算法步骤:
【Busacker-Grown迭代法】
见word文档
第六讲习题
1. 求图中的最大流
17
23 56 43 13 18 23
28 14
23
2.求图中所示网络的最小费用最大流, (b,c)中b表示容量,c表示费用
定义:对于网络N=(V,A,C),称定义在弧集A上的 函数f为网络N上的流;对于弧a,f(a)称为弧a上的 流量,若a=(Vi,Vj),f(a)也可以记作f(Vi,Vj)或者fij; 对于顶点v,记f+(v)为点v流出的流量,f-(v)为点v 流入的流量。 可行流:每个点的流量都小于等于容量,且 流出的流量等于流入的流量,则称为可行流; 最大流:可行流的最大值称为最大流。
1. D=(V,E)是一个有向图;
2. c是E上的正整数函数(容量函数),c(e)代表边e 的容量;
3. 记X为发点集(源),Y为收点集(汇),V-X-Y称为 中间点集。有向图D可记做(V,E,c,X,Y)
注意:根据网络的定义,对于任意一个有多个 收、发点的网络,可通过简单的方法转换为只 有一个发点和一个收点的网络。
定理: N中的流f是最大流当且仅当N不包含f可 增路。 最大流最小割定理:在任何网络中,最大流的 值等于最小割的容量。
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

网络流:最小费用最大流(最简单的算法)最小费用流在OI 竞赛中应当算是比较偏门的内容,但是NOI2008 中employee 的突然出现确实让许多人包括zkw 自己措手不及。

可怜的zkw 当时想出了最小费用流模型,可是他从来没有实现过,所以不敢写,此题0 分。

zkw 现在对费用流的心得是:虽然理论上难,但是写一个能AC 题的费用流还算简单。

先贴一个我写的employee 程序:只有不到70 行,费用流比最大流还好写~程序代码:C++#include <cstdio>#include <cstring>using namespace std;const int maxint=~0U>>1;int n,m,pi[550]={0},cost=0;bool v[550]={0};struct etype{int t,c,u;etype *next,*pair;etype(){}etype(int t_,int c_,int u_,etype* next_):t(t_),c(c_),u(u_),next(next_){}void* operator new(unsigned,void* p){return p;}} *e[550],*eb[550];int aug(int no,int m){if(no==n)return cost+=pi[1]*m,m;v[no]=true;for(etype *&i=e[no];i;i=i->next)if(i->u && !v[i->t] && pi[i->t]+i->c==pi[no])if(int d=aug(i->t,m<i->u?m:i->u))return i->u-=d,i->pair->u+=d,d;return 0;}bool modlabel(){int d=maxint,c;for(int i=1;i<=n;++i)if(v[i])for(etype *j=eb[i];j;j=j->next)if(j->u && !v[j->t])if((c=j->c-pi[i]+pi[j->t])<d)d=c;if(d==maxint)return false;for(int i=1;i<=n;++i)if(v[i])pi[i]+=d,e[i]=eb[i];return true;}int main(){freopen("costflow.in","r",stdin);freopen("costflow.out","w",stdout);scanf("%d %d",&n,&m);etype *Pe=new etype[m+m];while(m--){int s,t,c,u;scanf("%d%d%d%d",&s,&t,&u,&c);e[s]=new(Pe++)etype(t, c,u,e[s]);e[t]=new(Pe++)etype(s,-c,0,e[t]);e[s]->pair=e[t];e[t]->pair=e[s];}memmove(eb,e,sizeof(e));do do memset(v,0,sizeof(v));while(aug(1,maxint));while(modlabel());printf("%d\n",cost);return 0;}程序代码:CB大牛翻译的PASCALvarn,m,i,l,s,t,c,cost,u:longint;v:array[0..600]of boolean;dis:array[0..600]of longint;e_n,e_t,e_c,e_u,e_p,e_x:array[0..250000]of longint;function min(a,b:longint):longint;beginif a>b then exit(b);exit(a);end;procedure addedge(s,t,c,u,k:longint);begininc(l);e_n[l]:=e_n[s];e_n[s]:=l;//下一条边e_t[l]:=t;//边的另一端e_c[l]:=c;//边的费用e_u[l]:=u;//边的容量e_p[l]:=l+k;//对应的边end;procedure build(s,t,c,u:longint);beginaddedge(s,t,c,u,1);addedge(t,s,-c,0,-1);end;function aug(no,m:longint):longint;vari,d:longint;beginif no=n then begininc(cost,m*dis[1]);exit(m);end;v[no]:=true;i:=e_x[no];while i<>0 do beginif (e_u[i]>0)and(not v[e_t[i]])and(dis[e_t[i]]+e_c[i]=dis[no]) then begind:=aug(e_t[i],min(m,e_u[i]));if d>0 then begindec(e_u[i],d);inc(e_u[e_p[i]],d);e_x[no]:=i;exit(d);end;end;i:=e_n[i];end;e_x[no]:=i;exit(0);end;function modlabel:boolean;vard,i,j:longint;begind:=maxlongint;for i:=1 to n do if v[i] then beginj:=e_n[i];while j<>0 do beginif (e_u[j]>0)and(not v[e_t[j]])and(e_c[j]-dis[i]+dis[e_t[j]]<d) then d:=e_c[j]-dis[i]+dis[e_t[j]];j:=e_n[j];end;end;if d=maxlongint then exit(true);for i:=1 to n do if v[i] then beginv[i]:=false;inc(dis[i],d);end;exit(false);end;beginassign(input,'coflow.in');reset(input);assign(output,'coflow.out');rewrite(output);readln(n,m);l:=n;for m:=m downto 1 do beginreadln(s,t,u,c);build(s,t,c,u);end;repeatfor i:=1 to n do e_x[i]:=e_n[i];while aug(1,maxlongint)>0 do fillchar(v,sizeof(v),0);until modlabel;writeln(cost);close(output);end.这里使用的是连续最短路算法。

最短路算法?为什么程序里没有SPFA?Dijkstra?且慢,先让我们回顾一下图论中最短路算法中的距离标号。

定义为点的距离标号,任何一个最短路算法保证,算法结束时对任意指向顶点、从顶点出发的边满足(条件1),且对于每个存在一个使得等号成立(条件2)。

换句话说,任何一个满足以上两个条件的算法都可以叫做最短路,而不仅仅是SPFA、Dijkstra,算法结束后,恰在最短路上的边满足。

在最小费用流的计算中,我们每次沿的路径增广后都不会破坏条件1,但是可能破坏了条件2。

不满足条件2 的后果是什么呢?使我们找不到每条边都满足新的增广路。

只好每次增广后使用Dijkstra,SPFA 等等算法重新计算新的满足条件 2 的距离标号。

这无疑是一种浪费。

KM 算法中我们可以修改不断修改可行顶标,不断扩大可行子图,这里也同样,我们可以在始终满足条件1 的距离标号上不断修改,直到可以继续增广(满足条件2)。

回顾一下KM 算法修改顶标的方法。

根据最后一次寻找交错路不成功的DFS,找到,左边的点增加,右边的点减少。

这里也一样,根据最后一次寻找增广路不成功的DFS,找到,所有访问过的点距离标号增加。

可以证明,这样不会破坏性质1,而且至少有一条新的边进入了的子图。

算法的步骤就是初始标号设为,不断增广,如果不能增广,修改标号继续增广,直到彻底不能增广:源点的标号已经被加到了。

这样我们得到了一个简单的算法,只需要增广,改标号,各自只有7 行,不需要BFS,队列,SPFA,编程复杂度很低。

由于实际的增广都是沿最短路进行的,所以理论时间复杂度与使用SPFA 等等方法的连续最短路算法一致,但节省了SPFA 或者Dijkstra 的运算时间。

实测发现这种算法常数很小,速度较快,employee 这道题所有数据加在一起耗时都在2s 之内最小费用流的各种转化不少同学认为消圈算法比增广路算法使用面广,可以有负边,负圈,每个点都能有盈余亏空等等。

实际上增广路算法也是一样的。

以下讨论中为盈余量,为费用,为容量。

1).最小费用(可行)流最小费用最大流建立超级源和超级汇,对顶点,若添加边,若添加边,之后求从到的最小费用最大流,如果流量等于,就存在可行流,残量网络已在原图上求出。

2).最小费用最大流最小费用(可行)流连边,所有点有,然后直接求。

3).最小费用(可行)流中负权边的消除直接将负权边满流。

4.)最小费用最大流中负权边的消除先连边,使用(3.)中的方法消除负权边,使用(1.)中的方法求出最小费用(可行)流,之后距离标号不变,再求最小费用最大流;注意此时增广费用不能机械使用源点的标号——应该是源点汇点标号之差。

p.s.评测说明,最小费用流简单算法中的方法速度比SPFA 的实现快好多,推荐大家竞赛时使用网络流:最小费用最大流(PASCAL代码)具体的参见我的网络流总结这里说一种简洁高效的算法,来自ZKW神牛的原创。

性价比高,适合在竞赛使用。

即采用距离标号法,若能增广则无限增广,反之更新距离标号,直到无法更新为止。

const maxn=501; maxm=240001;type gtp=record y,d,c,op,next:longint; end;//========================================================var n,m,tot,st,ed,ans:longint;g:array[0..maxm] of gtp;first,now,dis:array[0..maxn] of longint;v:array[0..maxn] of boolean;//========================================================function min(a,b:longint):longint;beginif a>b then exit(b) else exit(a);end;//========================================================procedure add(x,y,d,c,t:longint); {存边表}begininc(tot);g[tot].y:=y; g[tot].d:=d; g[tot].c:=c;g[tot].op:=tot+t; g[tot].next:=first[x]; first[x]:=tot;end;//========================================================procedure init;var i,x,y,d,c:longint;beginfillchar(first,sizeof(first),0);readln(n,m); tot:=0; st:=1; ed:=n; ans:=0;for i:=1 to m dobeginreadln(x,y,d,c); {x点y点,容量,费用}add(x,y,d,c,1); add(y,x,0,-c,-1); {正反向都存}end;end;//========================================================function aug(x,flow:longint):longint; {寻找增广路}var y,t,tmp:longint;beginif x=ed then {找到增广路,增加流量}begininc(ans,flow*dis[st]);exit(flow);end;t:=now[x]; v[x]:=true;while t<>0 dobeginy:=g[t].y;if (g[t].d>0)and(not v[y])and(dis[x]=dis[y]+g[t].c) then {满足容量、未被访问(这里是一个优化)、距离标号关系}begintmp:=aug(y,min(g[t].d,flow)); {继续增广}if tmp>0 then {若增广成功}begindec(g[t].d,tmp); inc(g[g[t].op].d,tmp); {更新容量值}now[x]:=t; {更新当前弧}exit(tmp);end;end;t:=g[t].next;end;now[x]:=0; {小优化}exit(0);end;//========================================================function modlabel:boolean; {修改距离标号}var tmp,y,i,t:longint;begintmp:=maxlongint;for i:=1 to n doif v[i] then {若某边为true,则距离标号已经不合格}begint:=first[i];while t<>0 dobeginy:=g[t].y;if (g[t].d>0)and(not v[y])and(dis[y]+g[t].c-dis[i]<tmp) then tmp:=dis[y]+g[t].c-dis[i]; {取最小值}t:=g[t].next;end;end;if tmp=maxlongint then exit(true); {无法修复距离标号}for i:=1 to n do {若能修复,则修复距离标号}if v[i] thenbegininc(dis[i],tmp);v[i]:=false;end;exit(false);end;//========================================================procedure main;var i:longint;beginfillchar(dis,sizeof(dis),0);repeatfor i:=1 to n do now[i]:=first[i];while aug(st,maxlongint)>0 do fillchar(v,sizeof(v),false); {若能增广则无限增广} until modlabel; {直到无法修复距离标号}writeln(ans);end;//========================================================beginassign(input,'costflow.in'); reset(input);assign(output,'costflow.out'); rewrite(output);init;main;close(input); close(output); end.。

相关文档
最新文档