最大流的網絡,可看作為輔送一般貨物的運輸網絡,此時,最大流問題僅表明運輸網絡運輸貨物的能力,但沒有考慮運送貨物的費用。在實際問題中,運送同樣數量貨物的運輸方案可能有多個,因此從中找一個輸出費用最小的的方案是一個很重要的問題,這就是最小代價流所要討論的內容。
1.最小費用最大流問題的模型
給定網絡N=(V,E,c,w,s,t),每一弧(vi,vj)屬於E上,除了已給容量cij外,還給了一個單位流量的費用w(vi,vj)≥O(簡記為wij)。所謂最小費用最大流問題就是要求一個最大流f,使得流的總輸送費用取最小值。
W(f)=∑wijfij
2.用對偶法解最小費用最大流問題
定義:已知網絡N=(V,E,c,w,s,t),f是N上的一個可行流,p為vs到vt(關於流f)的可增廣路徑,稱W(p)=∑wij(p+)-∑wij(p-)為路徑p的費用。
若p*是從vs到vt所有可增廣路徑中費用最小的路徑,則稱p*為最小費用可增廣路徑。
定理:若f是流量為v(f)的最小費用流,p是關於f的從vs到vt的一條最小費用可增廣路徑,則f經過p調整流量Q得到新的可行流f’(記為f’=f+Q),一定是流量為v(f)+Q的可行流中的最小費用流。
3.對偶法的基本思路
①取f={0}
②尋找從vs到vt的一條最小費用可增廣路徑p。
若不存在p,則f為N中的最小費用最大流,算法結束。
若存在p,則用求最大流的方法將f調整成f*,使v(f*)=v(f)+Q,並將f*賦值給f,轉②。
4.迭代法求最小費用可增廣路徑
在前一節中,我們已經知道了最大流的求法。在最小費用最大流的求解中,每次要找一條最小費用的增廣路徑,這也是與最大流求法唯一不同之處。於是,對於求最小費用最大流問題余下的問題是怎樣尋求關於f的最小費用增廣路徑p。為此,我們構造一個賦權有向圖b(f),它的頂點是原網絡N中的頂點,而把N中每一條弧(vi,vj)變成兩個相反方向的弧(vi,vj)和(vj,vi)。定義w(f)中的弧的權如下:
如果(fij<cij),則bij=wij ;如果(fij=cij),則bij=+oo
如果(fij>0),則bji=-wij ;如果(fij=cij),則bji=+oo
於是在網絡N中找關於f的最小費用增廣路徑就等價於在賦權有向圖b(f)中,尋求從vs到vt的最短路徑。求最短路有三種經典算法,它們分別是Dijkstra算法、Floyd算法和迭代算法(ford算法)。由於在本問題中,賦權有向圖b(f)中存在負權,故我們只能用后兩種方法求最短路,其中對於本問題最高效的算法是迭代算法。為了程序的實現方便,我們只要對原網絡做適當的調整。將原網絡中的每條弧(vi,vj)變成兩條相反的弧:
前向弧(vi,vj),其容量cij和費用wij不變,流量為fij;
后向弧(vj,vi),其容量0和費用-wij,流量為-fij。
事實上,對於原網絡的數據結構中,這些單元已存在,在用標號法求最大流時是閑置的。這樣我們就不必產生關於流f的有向圖b(f)。同時對判斷(vi,vj)的流量可否改時,可以統一為判斷是否“fij<cij”。因為對於后向弧(vj,vi),若fij>0,則fji=-fij<0=cji。
例2:求輸送量最大且費用最小的運輸方案?
如下圖是一公路網,v1是倉庫所在地(物資的起點),v5是某一工地(物資的終點)每條弧旁的兩個數字分別表示某一時間內通過該段路的最多噸數和每噸物資通過該段路的費用。

程序如下:
{最小費用最大流參考程序}
program Maxflow_With_MinCost;
const
name1='flow.in';
name2='flow.out';
maxN=100;{最多頂點數}
type
Tbest=record {數組的結構}
value,fa:integer;
end;{到源點的最短距離,父輩}
Nettype=record
{網絡鄰接矩陣類型}
c,w,f:integer;
{弧的容量,單位通過量費用,流量}
end;
var
Net:array[1..maxN,1..maxN] Of Nettype;
{網絡N的鄰接矩陣}
n,s,t:integer;{頂點數,源點、匯點的編號}
Minw:integer;{最小總費用}
best:array[1..maxN] of Tbest;{求最短路時用的數組}
procedure Init;{數據讀人}
var inf:text;
a,b,c,d:integer;
begin
fillchar(Net,sizeof(Net),0);
Minw:=0;
assign(inf,name1);
reset(inf);
readln(inf,n);
s:=1;t:=n;{源點為1,匯點n}
repeat
readln(inf,a,b,c,d);
if a+b+c+d>0 then
begin
Net[a,b].c:=c;{給弧(a,b)賦容量c}
Net[b,a].c:=0;{給相反弧(b,a)賦容量0}
Net[a,b].w:=d;{給弧(a,b)賦費用d}
Net[b,a].w:=-d;{給相反孤(b,a)賦費用-d}
end;
until a+b+c+d=0;
close(inf);
end;
function Find_Path:boolean;
{求最小費用增廣路函數,若best[t].value<>MaxInt,
則找到增廣路,函數返回true,否則返回false}
var Quit:Boolean;
i,j:Integer;
begin
for i:=1 to n do best[i].value:=Maxint;best[s].value:=0;
{尋求最小費用增廣路徑前,給best數組賦初值}
repeat
Quit:=True;
for i:=1 to n do
if best[i].Value < Maxint then
for j:=1 to n do
if (Net[i,j].f < Net[i,j].c) and
(best[i].value + Net[i,j].w <
best[j].value) then
begin
best[j].value:=best[i].value + Net[i,j].w;
best[j].fa := i;
Quit:= False;
end;
until Quit;
if best[t].value<Maxint then find_path:=true else find_path:=false;
end;
procedure Add_Path;
var i,j: integer;
begin
i:= t;
while i <> s do
begin
j := best[i].fa;
inc(Net[j,i].f); {增廣路是弧的數量修改,增量1}
Net[i,j].f:=-Net[j,i].f;{給對應相反弧的流量賦值-f}
i:=j;
end;
inc(Minw,best[t].value); {修改最小總費用的值}
end;
procedure Out;{輸出最小費用最大流的費用及方案}
var ouf: text;
i,j: integer;
begin
assign(ouf,name2);
rewrite(ouf);
writeln(ouf,'Min w = ',Minw);
for i := 1 to n do
for j:= 1 to n do
if Net[i,j].f > 0 then
writeln(ouf,i, ' -> ',j,': ',
Net[i,j].w,'*',Net[i,j].f);
close(ouf);
end;
Begin {主程序}
Init;
while Find_Path do Add_Path;
{當找到最小費用增廣路,修改流}
Out;
end.
flow.in如下:
5
1 2 10 4
1 3 8 1
2 4 2 6
2 5 7 1
3 2 5 2
3 4 10 3
4 5 4 2
0 0 0 0
運行結果flow.out如下:
Min w = 55
1 -> 2: 4*3
1 -> 3: 1*8
2 -> 5: 1*7
3 -> 2: 2*4
3 -> 4: 3*4
4 -> 5: 2*4
源自:OIBH
