关于最小费用最大流的解题
- 格式:doc
- 大小:116.00 KB
- 文档页数:3
网络流:最小费用最大流(最简单的算法)最小费用流在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.这里使用的是连续最短路算法。
最小费用最大流问题例题讲解
最小费用最大流问题(Minimum Cost Maximum Flow Problem)是一种在特定的多媒体网络中传送给定体积的流量,使总花费最小化的一种算法。
它能满足一些实际生活中的求解,比如电力系统的供求、工厂的物料的分配和两地之间的物品的运输问题,以及更加复杂的产品开发和行业分工中的分布问题等等。
最小费用最大流问题的目标是在满足给定的最大流量要求的前提下,找出具有最小成本的流量方案。
这种问题的解决步骤如下:
1. 在图形中定义网络:用图形表示整个网络,每条边的容量是边上的流量上限。
2. 尝试找出最大流量:在不超过容量限制的前提下,找出输出流量最大的允许方案,也就是最小费用最大流量。
3. 计算最小成本:对所有边的成本进行总结,计算出最小成本。
下面以一个最小费用最大流问题的例题来说明:
假设有一个三角形的网络,它由一个源点S、一个汇点T、一个中间点O以及三条边组成,边的名字分别是SO、OT、OS,它们的容量分别是10、15和5,费用分别是5、3和2。
要求我们在此条件下求解最小费用最大流问题。
解:首先,我们可以求出最大流量:在边SO的容量为10时,我们可以将费用最小的边OT累加,得到最大流量值为10+3=13。
接下来,计算最小费用:根据上述算法,所有边的费用应该都大于等于0,才能累加而得到最大流量。
也就是说,最小费用为
5+3+2=10。
最后,最小费用最大流问题的解为:最大流量13,最小成本10。
图论专题小结:最小费用最大流算法一,给定流量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.二,网络输出最大流时,求出最小的费用这就是最小费用最大流问题:既要求出最大流,又要求出达到最大流时候的最小费用。
利用lingo程序求最小费用最大流通常求最小费用最大流问题是分两个阶段:1,先求最大流。
2,再在最大流的基础上求最小费用流。
以下图为例。
其中如(5,8)=(容量,费用)。
求从S到T的最小费用最大流。
1,先求最大流,lingo程序为:MODEL:sets:nodes/s,1,2,3,t/;arcs(nodes,nodes)/s,1 s,2 1,t,1,3 2,1 2,3 3,t/:c,f;endsetsdata:c= 5 8 4 3 2 10 8;enddatamax = flow;@for(nodes(i)|i #ne# 1 #and# i #ne# @size(nodes):@sum(arcs(i,j):f(i,j))-@sum(arcs(j,i):f(j,i))=0);@sum(arcs(i,j)|i #eq# 1:f(i,j)) = flow;@for(arcs:@bnd(0,f,c));END结果是,最大流为12,2,再在最大流的基础上求最小费用流。
程序为:MODEL:sets:nodes/s,1,2,3,t/: ;arcs(nodes,nodes)/s,1 s,2 1,t,1,3 2,1 2,3 3,t/:b,c,f;endsetsdata:flow=12;b=8 7 9 2 5 9 4 ;c=5 8 4 3 2 10 8;enddatamin=@sum(arcs:b*f);@for(nodes(i)|i #ne# 1 #and# i #ne#@size(nodes):@sum(arcs(i,j):f(i,j))-@sum(arcs(j,i):f(j,i))=0);@sum(arcs(i,j)|i #eq# 1:f(i,j)) = flow;@for(arcs:@bnd(0,f,c));END结果是:Global optimal solution found.Objective value: 218.0000Infeasibilities: 0.000000Total solver iterations: 0Variable Value Reduced CostF( S, 1) 5.000000 -6.000000F( S, 2) 7.000000 0.000000F( 1, T) 4.000000 0.000000F( 1, 3) 3.000000 0.000000F( 2, 1) 2.000000 -2.000000F( 2, 3) 5.000000 0.000000 F( 3, T) 8.000000 -3.000000现利用lingo的子模型功能,将2个程序合二为一,可直接算出最小费用流。
最小费用最大流问题解答
【王宇鑫 10111856】
分布求出1到6的通路,并且计算每一种的费用和流量。
(1)1→3→5→6 (2)1→2→4→6
单位费用为2+2+6=10 单位费用为8+3+7=18 饱和流量为5 饱和流量为7
(3)1→3→2→4→6 (4)1→3→2→4→5→6
单位费用为2+5+3+7=17 单位费用为2+5+3+4+6=20 饱和流量为5 饱和流量为5 (5)1→3→5→2→4→6 (6)1→2→4→5→6
单位费用为2+2+1+3+7=15 单位费用为8+3+4+6=21 饱和流量为2 饱和流量5 则可以获得下表:
由此可知:
当05f <≤时,选择1号路线;
当57f <≤时,选择1号与2号混合,流量超过5的部分使用2号路线;
当78f <≤时,选择1号、2号与3号,超过7的部分从3号路走(3号由于1→3路径只能流经1单位的流量);
当814f <≤时,选择1号、2号、3号与4号路线,8~14的部分从4号走;
此时已经饱和,在无法加大流量了。
则最大流量为14
⨯+⨯+⨯+⨯=+++=
最小费用为510215117618503017108205画出分段的图像
不同流量下的成本曲线
则最后的结果图为:。
最⼩费⽤最⼤流:⼆分答案(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%。
关于最小费用最大流的解题
这是课本235页的一道习题
题目是:求如下图中网络的最小费用最大流。
弧旁数字(Cij,Rij)。
根据之前的例题14
解题过程如下:
(1)构造一个对偶网络,按狄克斯屈标号法找到最短路,得到相应的增广链,调整;
(2)再次构造一个对偶网络,如下图。
在书中曾提到在含负权的的网络中不能运用狄克斯屈标号法,只能用距离矩阵摹乘法。
可是距离矩阵摹乘法做起来确实是比较复杂,因此我在研究已经做过的几个题中发现,我们在计算最小费用最大流的时候,可以用如下方法来解该题。
解:
从S点出发,有三条路,分别到1和2,由于标号为-1的路是反向的,排除。
因此,我们选标号比较小的S—1这条路……依次类推,得到一条最短路:S—1—2
—4—3—T。
再按之前的解题思路解题。
再得到S—2—4—3—T;
不存在从S到T的最短路,故最大流为f(X*)=4+1=5,c(X′)=3×1+4×2+1×2+2×5+1×4+3×3+1×1=37;
(3)重复上面的动作;
(4)得到最小费用最大流。
我不喜欢复杂的做题的方法,因此,在做这个题的时候,由于之前提到过的狄克斯屈标号法不能用于含负权的网络图中,所以必须用到距离矩阵摹乘法,而距离矩阵摹乘法确实是很麻烦,又得画表,又得计算。
所以,我通过书上的图列推出这样来做这个题。
虽然并不一定这种做法是不是对的(我目前只在几个题目
中运用,结果是对的),但我相信这样一个方向是对的,以前的运筹学方法也是前辈们研究出来的。
另外,我还在维普中文网上看到了一篇题为《含负权最短路问题的一个改进标号法》的论文,载要:在不出现负回路的情况下,给出了在赋权的网络图中求两点之间的最短路问题的一个改进标号法,该方法对于网络图中出现负权的情况也有效。
最后给出了该算法的数值实验结果。
这篇文章中,提到了对狄克斯屈标号法的在负权网络不能运用的缺陷的改进。
这让我体会到了运筹学学习当中的另一个重要方面:我们不能拘泥于课本上一层不变的解题方法,应多了解运筹学发展的最新动态,掌握有关运筹学的最新知识。
通过对其的研究,从而找到更好的方法来解决相关的问题。
这是我在这次作业当中,所获得的心得体会。