最小費用最大流(講解+模板)
阿新 • • 發佈:2018-12-24
問題引入:最小費用最大流問題是經濟學和管理學中的一類典型問題。在一個網路中每段路徑都有“容量”和“費用”兩個限制的條件下,此類問題的研究試圖尋找出:流量從A到B,如何選擇路徑、分配經過路徑的流量,可以在流量最大的前提下,達到所用的費用最小的要求。如n輛卡車要運送物品,從A地到B地。由於每條路段都有不同的路費要繳納,每條路能容納的車的數量有限制,最小費用最大流問題指如何分配卡車的出發路徑可以達到費用最低,物品又能全部送到。
解決最小費用最大流問題,一般有兩條途徑。一條途徑是先用最大流演算法算出最大流,然後根據邊費用,檢查是否有可能在流量平衡的前提下通過調整邊流量,使總費用得以減少?只要有這個可能,就進行這樣的調整。調整後,得到一個新的最大流。
然後,在這個新流的基礎上繼續檢查,調整。這樣迭代下去,直至無調整可能,便得到最小費用最大流。這一思路的特點是保持問題的可行性(始終保持最大流),向最優推進。另一條解決途徑和前面介紹的最大流演算法思路相類似,一般首先給出零流作為初始流。這個流的費用為零,當然是最小費用的。然後尋找一條源點至匯點的增流鏈,但要求這條增流鏈必須是所有增流鏈中費用最小的一條。如果能找出增流鏈,則在增流鏈上增流,得出新流。將這個流做為初始流看待,繼續尋找增流鏈增流。這樣迭代下去,直至找不出增流鏈,這時的流即為最小費用最大流。這一演算法思路的特點是保持解的最優性(每次得到的新流都是費用最小的流),而逐漸向可行解靠近(直至最大流時才是一個可行解)。
由於第二種演算法和已介紹的最大流演算法接近,且演算法中尋找最小費用增流鏈,可以轉化為一個尋求源點至匯點的最短路徑問題,所以這裡介紹這一演算法。
【計算步驟】 ⒈ 對網路G=[V,E,C,W],給出流值為零的初始流。 ⒉ 作伴隨這個流的增流網路G′=[V′,E′,W′]。G′的頂點同G:V′=V。若G中f(u,v)0,則G′中建邊(v,u),w′(v,u)=-w(u,v)。 ⒊ 若G′不存在x至y的路徑,則G的流即為最小費用最大流, 停止計算;否則用標號法找出x至y的最短路徑P。 ⒋ 根據P,在G上增流:對P的每條邊(u,v),若G存在(u,v),則(u,v)增流;若G存在(v,u),則(v,u)減流。增(減)流後,應保證對任一邊有c(e)≥ f(e)≥0。 ⒌ 根據計算最短路徑時的各頂點的標號值L(v),按下式修 改G一切邊的權數w(e): L(u)-L(v)+w(e)→w(e)。 ⒍ 將新流視為初始流,轉2。 這裡仍然採用EK演算法,雖然複雜度較高,但是一般問題還是可以解決的。 和最大流演算法的唯一區別是假如了最短路spfa演算法(相信不難理解) 舉一個模板題程式碼說明問題 程式碼如下:#include<stdio.h> #include<string.h> #include<queue> #include<stdlib.h> #include<math.h> #include<limits.h> #include<algorithm> using namespace std; #define maxn 205 struct node { int x,y; }hdis[maxn],pdis[maxn]; struct road { int flow; int cost; }path[maxn][maxn]; char s[maxn][maxn]; int pre[maxn]; int link[maxn]; int used[maxn]; int best[maxn]; int ans,n,m; int spfa(); void operat(); void operat() { ans=0; int end=n+m+1; while(spfa()) { int x=end; int dis=INT_MAX; while(x) { int vlink=link[x]; dis=min(dis,path[vlink][x].flow); x=vlink; } x=end; while(x) { int vlink=link[x]; path[vlink][x].flow-=dis; path[x][vlink].flow+=dis; ans+=dis*path[vlink][x].cost; x=vlink; } } printf("%d\n",ans); } int spfa() { int start=0,end=n+m+1; memset(used,0,sizeof(used)); memset(link,-1,sizeof(link)); memset(best,1,sizeof(best)); queue<int>q; q.push(0); best[0]=0; while(!q.empty()) { int now=q.front(); q.pop(); for(int i=1;i<=end;i++) { if(path[now][i].flow>0 && best[i]>best[now]+path[now][i].cost) { best[i]=path[now][i].cost+best[now]; link[i]=now; if(!used[i]) { used[i]=1; q.push(i); } } } used[now]=0; } if(best[end]<=100000) return 1; return 0; } int main() { int i,j; while(scanf("%d%d",&n,&m),n!=0 && m!=0) { memset(path,0,sizeof(path)); int a=0,b=0; for(i=1;i<=n;i++) { for(j=1;j<=m;j++) { scanf(" %c",&s[i][j]); if(s[i][j]=='H') { hdis[++b].x=i; hdis[b].y=j; } if(s[i][j]=='m') { pdis[++a].x=i; pdis[a].y=j; } } } n=a;m=b; int x; for(i=1;i<=n;i++) { for(j=1;j<=m;j++) { x=abs(hdis[j].x-pdis[i].x)+abs(hdis[j].y-pdis[i].y); path[i][j+n].flow=1; path[i][j+n].cost=x; path[j+n][i].cost=-x; } } for(i=1;i<=n;i++) path[0][i].flow=1; for(i=1;i<=m;i++) path[n+i][n+m+1].flow=1; operat(); } }