制造网络的一个最小费用最大流算法
最小费用最大流

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.这里使用的是连续最短路算法。
最小费用最大流问题的算法_运筹学_[共7页]
![最小费用最大流问题的算法_运筹学_[共7页]](https://img.taocdn.com/s3/m/07f655bf7e21af45b207a88c.png)
∑ ∑ cij − cij 。称 Δ(c μ)是沿增广链 μ 当可行流增加单位流值时费用的增量。简称为增广链 μ
u+
u−
的单位费用增量。
可以证明,若 X 是流量为 (f X)的所有可行流中费用最小者,而 μ 是关于 X 的所有增广
链中费用最小的增广链,则沿 μ 去调整 X ,得到的可行流 X ′ 就是流量为 (f X ′)的所有可行流
中的最小费用流。这样,当 X ′ 是最大流时,它也是我们所要寻找的最小费用最大流了。
注意到 cij ≥ 0 ,故 X = 0 必是流量为 0 的最小费用流。这样,总可以从 X = 0 开始。一般 地,若已知 X 是流量 (f X)的最小费用流,为了寻求关于 X 的最小费用增广链,我们构造一
个赋权有向图 D(X),它的顶点是原网络 D 的顶点,而把 D 中的每一条弧(vi,v j)变成两个相
反方向的弧(vi,v j)和(vj,vi),定义 D(X)中弧的权 wij′ :
wij′
=
w(′ vi
,v j)=
⎧⎪⎨⎪⎩c+i∞j ,若,若xijxi<j =wwij ij
,
w
′
ji
=
w(′ v
j,vi)=
⎨⎪⎩⎧⎪+−∞cij,,若若xxijij
> 0
0
在 D(X)中长度为 +∞ 的弧可以略去。
故在网络 D 中寻找关于 X 的最小费用增广链就等价于在赋权有向图 D(X)中,寻找从 v1 到 vn 的最短路。这样,我们有如下算法。
第 6章 图 与 网 络 分 析
181
运
筹
Step1 确定初始可行流 X (0) = 0 ,令 k := 0 ;
最小费用最大流问题.

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
制造网络的一个最小费用最大流算法

% 代表 流入 点 的流量 . 意到在 一个 5点处 仅 有一 种原 材料 . 注 (i 点 终 点代表 最后 的产 品 , i) i 一个 制造 网络 流 G可能 有一 个或 几个 点 , 每一 个代 表一 个 不 同的最 后 的产 品 . 坼 是所 有 点 的集合 , 每一个 ∈ 坼 , ()= . 令 对 Li %=
j ( ) ∈
, ∈ Ⅳ7兢 代表 流 出 点 的流 量 ) vi ' ( .
() 4
( ) i D点 D点也称为蒸馏点 . v 一个 D点只有一条入弧( 一种材料 )但有多个出弧( , 不同种产品)且流 , 经出弧的流值和流经入弧的流值成比例. 令 是所有的 D点的集合 . 对于每一个 ∈ % , 有
维普资讯
第6 期
张远福 , : 等 制造网络的一个最小费用最大流算 法
1 制造 网络 的一个 最小 费用最大流算法
我 们考 察一 个简 化情 况 : 一个 制造 网络 G = ( A, )只有 一个 源点 ( 点 )通过 0点 及 D点 分 布一 Ⅳ, , 发 , 种产 品到 各个 不 同的收 点 , 流经 每个 弧 ( ,) i 的流量有 一 个上 界 0≤ ≤ I ,s=+∞. t I t 每一条 弧 ( ,) A i ∈ 除 了已给容 量 u ’ 0外 , 给 出了一个 单位 流 量 的费用 c ≥ 0 q≥ 还 f .
于 的所有 增广 链 中费用 最 小 的增 广 链 , 么沿 着 去 调整 , 到 的可行 流 , 那 得 就是 流量 为 的所有 可行
流 中的最小 费用 流 . 当 是 最大 流 时 , 就是 所要 求 的最小 费 用最 大流 了 【 . 它 2 J
由于 c ≥ 0所以 X =0 i , 必是流量为 0 的最小费用流 , 这样总可以从 X =0 开始对流值进行增广来求得 费用最小的最大流. 一般地 , 已知流为 X = 的最小费用流 , 设 余下的问题就是如何寻找关于 的最小增广 链 . 此 , 构造 一个 赋权 有 向图 ( , 的顶 点 和弧是 原 网络 G 的顶 点 和弧 , 为 可 )它 定义 ( )中弧 的权 为
图论专题小结:最小费用最大流算法

图论专题小结:最小费用最大流算法一,给定流量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.二,网络输出最大流时,求出最小的费用这就是最小费用最大流问题:既要求出最大流,又要求出达到最大流时候的最小费用。
图与网络模型_最小费用最大流问题
−bij +∞
当
f
(K ij
−1
)>
0
当
f
(K ij
−1
)=0
上述定义式的含义是:
零流弧(
f
(K ij
−1)=0
)上,在其对应位置构造与其方向相同且费用为
bij
的弧;
对于饱和弧(
f
(K ij
−1)=c ij
),在其对应位置构造与其方向相反且费用为-bij
的弧;
对于非饱和且非零流弧(
0<
f
(K−1) ij
因 f (s22)=2< cs2=10 ,所以, w(s22)=bs 2=4 。因 f (s22)>0 ,所以, w(22s)=−bs2=−4 。
因 f (s23)=5< cs3=8 ,所以, w(s23)=bs 3=1 。因 f (s23)>0 ,所以, w(32s)=−bs 3=−1 。
因 f 3(22)=5=c32=5 ,所以, w(322)=+∞ 。因 f 3(22)>0 ,所以, w(223)=−b32=−2 。
根据公式:
{ w(ijK −1)=
bij +∞
当
f
(K−1) ij
<
c
ij
当
f
= (K−1)
ij
cij
{ w(jKi −1)=
−bij +∞
当
f
(K ij
−1
)>
0
当
f
(K ij
−1
)=0
因为对任意(vi,vj), f i(j0)=0 ,所以 w(ij0)= bij , w(j0i )=+∞ 。
最小费用最大流简介
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的前驱
最小费用最大流:二分答案(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%。
网络系统的最小费用最大流问题
网络系统的最大流问题
• 任意一个网络上的可行流总是存在的。例如零流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、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
收稿日期:2007203222基金项目:江西省教育厅基金资助项目(JX JG 2O6217216).作者简介:张远福(19632),男,江西九江人,理学硕士,主要从事组合最优化的研究.文章编号:100025862(2007)0620622203制造网络的一个最小费用最大流算法张远福, 谭毓澄, 余剑敏(九江学院理学系,江西九江 332000)摘要:制造网络流广泛应用于解决水源的调度及工厂的产品运输、分配、合成等问题.该文提出一个制造网络流的最小费用最大流算法.关键词:制造网络流问题;最小费用最大流;层数中图分类号:O 224 文献标识码:A文献[1]中首次提出了制造网络流的一个最小费用流算法.本文则提出一个制造网络流的最小费用最大流算法.令G =(N,A,U)是一个网络,N 表示点集,A 表示弧集,U ={u i j },u ij 表示弧(i,j)的容量.对于一个弧(i,j)I A,用x ij 表示流过弧(i,j )的流值,u ij 为x i j 的上界0[x ij [u ij ,P (i ,j)I A.(1)对于一个点i I N ,定义E (i)={j I N:(j,i)I A},L(i)={j I N :(i,j)I A}.在制造网络流模型中,有6种点.( )O 点 对于O 点有6j I E(i)x ji =6j I L(i )x ij ,P i I N o ,N o 代表所有O 点的集合.(2)( )S 点 即原材料的来源点.令N s 代表S 点的集合,对于每一个i I N s ,E (i)=ª,x i =6j I L(i)x ij ,P i I N s .(3)x i 代表流入i 点的流量.注意到在一个S 点处仅有一种原材料.( )T 点 终点代表最后的产品,一个制造网络流G 可能有一个或几个T 点,每一个代表一个不同的最后的产品.令N T 是所有T 点的集合,对每一个i I N T ,L(i)=ª.x i =6j I E(i)x ji ,P i I N T (x i 代表流出i 点的流量).(4)( )D 点 D 点也称为蒸馏点.一个D 点只有一条入弧(一种材料),但有多个出弧(不同种产品),且流经出弧的流值和流经入弧的流值成比例.令L D 是所有的D 点的集合.对于每一个i I N D ,有E (i)={i *},x i j =K ij x i *i ,P j I L(i)(K i j 是正的实数).(5)( )I 点 满足如下条件的为I 点集合:6j I E(i)x ji =x i +6j I L(i )x ij ,x i \0,P i I N I ,(6)x i 代表过剩的流量在i 点存储下来.( )C 点 C 点也称合成点,一个C 点仅有一个出弧(一种产品)和多个入弧(不同材料).流经入弧的流值和流经出弧的流值成比例.令N C 是所有C 点的集合,对每一个i I N C ,有L(i)={i *},x ji =h ji x ii *,P j I E (i)(h ji 是正实数).(7)第31卷第6期2007年11月 江西师范大学学报(自然科学版)J O URN A L OF JIA NG XI N ORM AL UNIVERSITY(NA T URA L SCIE NCE)Vol.31N o.6 Nov.20071 制造网络的一个最小费用最大流算法我们考察一个简化情况:一个制造网络G =(N ,A,U),只有一个源点(发点),通过O 点及D 点分布一种产品到各个不同的收点,流经每个弧(i,j)的流量有一个上界0[x ij [u ij ,u s =+].每一条弧(i,j)I A 除了已给容量u ij \0外,还给出了一个单位流量的费用c i j \0.所谓最小费用最大流问题就是要求一个最大流x =6j I N t x j ,满足(1)~(5)的条件,且满足6j I L(i)K ij =1,P i I N D ,使流的总运输费用6(i ,j)I A c ij x i j 最小.下面给出制造网络的一个最小费用最大流算法.若X 是流量为x 的所有可行流中费用最小者,而L 是关于X 的所有增广链中费用最小的增广链,那么沿着L 去调整x,得到的可行流x c ,就是流量为x c 的所有可行流中的最小费用流.当x c 是最大流时,它就是所要求的最小费用最大流了[2].由于c i j \0,所以X =0必是流量为0的最小费用流,这样总可以从X =0开始对流值进行增广来求得费用最小的最大流.一般地,设已知流为X =x 的最小费用流,余下的问题就是如何寻找关于X 的最小增广链.为此,可构造一个赋权有向图X (x),它的顶点和弧是原网络G 的顶点和弧,定义X (x)中弧的权X ij 为X i j =c i j , x i j <u ij ,+],x ij =u i j ,此时去掉(i,j)弧.在赋权有向图X (x)中,对其各点进行标号,对于D 点,用3个标号来表征,第1个标号是层数,第2个标号是费用,第3个标号是容许通过的最大流.对于其他各点,我们用2个标号来表征,第1个标号是层数,第2个标号为从该点流出的单位流最小费用.引理1[3] 设有向图G =(N ,A)不含回路,则总可以把G 的顶点重新编号1,2,,,n,使得对一切i <j (j,i)| A.先对赋权有向图X (x)的各点进行第1个标号.层数的计算方法:令点S 的层数为0,R ={s,1,2,,},M =ª.Step1 置M =M G {s},R =R \s,若R =ª,停,否则在R 中去掉点s 及其出弧,存在点i m 没有入弧,则i m 标为layer 1;Step2 设i k 已标号layer j ,置M =M G {i k },R =R \{i k },若R =ª,结束,否则在R 中去掉i k 及其出弧,存在i m 没有入弧,标i m 为layer (j +1);Step3 置j =j +1,转step 2.这样得到了各点的第1个标号.把从D i 流出的流所能到达的各点{D i ,i 1,,,i n }和弧称为一个D i 系统.下面对赋权有向图X (x)各点进行第2个标号,即表示从点i 流出的单位流的最小费用c i .计算方法如下(从最大层数的点开始).Step0 把没有出弧的各收点标为c t =0;Step1 若点k(k =1,2,,)已标号,点i |D 未标号,则标点i 的c i =min i I E(k){c ik +c k },点i I D 且未标号,则标D i 的最小费用标号为c D i =6j I L(i)c j +6(i,j)I A D 系统(i ,j)I D 点出弧k ij c i j .这样得到了赋权有向图X (x)各点第2个标号.下面对D i 各点进行第3个标号,第3个标号表示通过点D i 容许增加的最大流值x D i .为了计算各D i 容许增加的最大流值x D i ,对网络X (x)中D i 系统各点进行标号.用反向标号法:从最远的层数(层数最大的点)开始.Step0 把没有出弧的点标号为];Step1 若点k(k =1,2,,)已标号,i 未标号.若i I N O ,则给i 标上x i :x i =6(i ,k)I A min {(u ik -x ik ),x k }(i,k)I A;623第6期张远福,等:制造网络的一个最小费用最大流算法若i I N D ,则标号i 为x i :x i =min k({u ik -x ik ),x k })/K ik ).这样得到了赋权有向图X (x )中各D i 容许增加的最大流值x D i 的标号,此时只保留D 点的标号,去掉D系统其他点的标号.把从S 点沿着弧不经过D 点到达的已标号的D i 点标为红色.令从S 点到标有红色的D i 的有向路p 的费用为:6(i,j)I p (i,j)|D i 系统c ij +D i 的第2个标号.定理1 网络中G =(N ,A,U)中寻求关于x 的最小费用增广链等价于在赋权有向图X (x)中,寻求从点S 不经过D 点到达各t 点的最短有向路和从S 点到标有红色的D 点的最短有向路的最短路.证 从赋权有向图X (x)的定义直接得到.下面给出网络G =(N ,A,U)的最小费用最大流算法.Step0 取0流X 作为初始可行流;Step1 构造赋权有向图X (x),对其各点进行第1个标号;Step2 在构造赋权有向图X (x)中对各点进行第2个标号和对各D 点进行第3个标号,把从S 点沿着弧不经过D 点到达的已标号的D i 点标为红色.令M ={D 1,D 2,,,D n c },R =ª;Step3 在赋权有向图X (x)中,寻找从S 到M 中各标有红色的D 点的最短有向路和从S 点不经过D 点到达的各t 点的最短有向路,并加以比较,找出最短路P ;Step4 对最短路的流进行增广.若最短路P 是从S 到红色的D i ,则在原网络G =(N ,A,U)中增加流值x 增广=min (i ,j)I P {u i j -x ij ,x c D i },记M =M \{D i },R =R G {D i }.若最短路P 是从S 不经过D 点到达的收点t i ,则增加流值x 增广=min (i ,j)I P {u i j -x ij },转step 1.2 复杂性分析设网络G 的顶点数为n,弧数为m,其中D 点的个数为n c ,给各点标号的复杂性为:求第1个标号层数的的复杂性为o(n)[4],求第2个标号即流经各点单位流的费用的复杂性为o(mn),求D 系统容许增加的最大流的第3个标号复杂性为o(mn 2),故给赋权有向图X (x)各点标号的复杂性为o(n )+o(mn)+o(mn 2)=o(mn 2),利用Dijkstra 算法求最短路的复杂性为o(n 2),所以本算法的复杂性为o(mn 4n c ).参考文献:[1]Shu 2cheng Fang,Liq un Qi.Manufacturi ng netwo rk flo w:a generalized netwo rk flo w model for manufacturing process mo deling[J].Opti 2mization Metho ds and so ft ware,2003,18:1432165.[2]谢政,李建平.网络算法与复杂性理论[M].北京:国防大学出版社,1995.[3]谢金星,邢文训.网络最优化[M].北京:清华大学出版社,2000.[4]Ed monds J,Karp R M.Theorical impro vements in alogrithmic efficiency for netwo rk flo w pro blo m[J].A CM,1972,19:2182264.The Algorithm of Maximum Flow and Minimum Cost for Manufacturing NetworkZ HA NG Yuan 2f un, TA N Y u 2cheng, Y U Jian 2min(Department of Technology,Jiujiang C ollege,Ji ujiang Jiangxi 332000,China)Abstract:Manufacturing netw ork flo w proble m is applied widely in the distribution of the source of water and the transit of produc ts tha t have some process of synthesis of different materials to one products and/or the distilling of one material to many diff erent products.In this paper,I present an algorithm of maximum manuf acturing netw ork flow and minimum cost.Key words :manufac turing network problem;maximum flow and minimum cost;layer (责任编辑:王金莲)624江西师范大学学报(自然科学版)2007年。