1、什么是最小費用最大流問題
上篇文章我們講解了最大流問題,那什么是最小費用最大流呢?聽名字就可以看出,我們要在滿足最大流的同時找到達成最大流的最小費用。
對於一個網絡流,最大流是一定的,但是組成最大流的費用是可以不同的,這里就有了在最大流網絡上產生的費用流網絡,就有了最小花費問題。
簡單來說,就是滿足最大流的路徑可能有多條,我們要從這多條路徑中找到一條花費代價最小的路徑。所以最大流是解決這類問題的前提
2、EK算法 + SPFA 最短路
我們用每條邊單位流量的花費作為邊權,假如一條合法路徑上每條邊的花費分別為 c1,c2,.......ck , 並且這條邊上的最小流量為flow,
那么這條路徑上的花費為 : c1 * flow + c2*flow + ..... + ck*flow = (c1+ c2 + c3 + .... + ck)*flow = dis [ci] * flow
這里的 dis[ci] 就是我們要求的最短路!
3、算法思想
采用貪心的思想,每次找到一條從源點到達匯點的路徑,增加流量,且該條路徑滿足使得增加的流量的花費最小,直到無法找到一條
從源點到達匯點的路徑,算法結束。
由於最大流量有限,每執行一次循環流量都會增加,因此該算法肯定會結束,且同時流量也必定會達到網絡的最大流量;同時由於每次都是增加的最小的花 費,即當前的最小花費是所有到達當前流量flow時的花費最小值,因此最后的總花費最小。
4、求解步驟
1、找到一條從源點到達匯點的花費最小的路徑,該花費 = 使用該路徑上的邊的單位費用之和。2、然后找出這條路徑上的邊的容量的最小值f,則當前最大流 max_flow 擴充f(求最大流的過程),同時當前最小費用 min_cost 擴充 f*min_dist(s,t)。
3、更新路徑流量 flow[][] ,將這條路徑上的每條正向邊的容量都減少f,每條反向邊的容量都增加f。
4、重復(1~3)直到無法找到從源點到達匯點的路徑。
5、需要注意幾點
1、注意超級源點和超級終點的建立。這里的超級源點/匯點的建立是把所有邊統一起來,構成一張網絡圖,因為有些題目不會直接給你網絡圖,
需要你自己去建立,例如 hdu 1533
2、初始化時,正向邊的單位流量費用為cost[u][v],那么反向邊的單位流量費用就為 -cost[u][v]。正向邊和反向邊的費用互為相反數,因為回流費用減少。
3、費用cost數組和容量cap數組每次都要初始化為0。
4、求解從源點到匯點的“最短”路徑時,由於網絡中存在負權邊,因此使用SPFA來實現。
6、代碼
int cap[500][500],cost[500][500],flow[500][500];//cap是容量,cost是花費,flow是流量 int pre[500],dis[500],vis[500],cnt[500];//pre是記錄增廣路的前驅節點,dis[i]是記錄源點到節點i的最小距離 //vis[i]標記點是否在隊列中,cnt[i]記錄點i進入隊列的次數 int n,m; int st,endd; int mn_cost,mx_flow; int spfa() { for(int i=0;i<n;i++) dis[i]=mx; memset(vis,0,sizeof(vis)); memset(cnt,0,sizeof(cnt)); queue<int>p; p.push(st);//st是起點 vis[st]=1; cnt[st]=1; dis[st]=0; while(!p.empty()) { int now=p.front(); p.pop(); vis[now]=0; for(int i=0;i<n;i++) { if(cap[now][i]>flow[now][i]&&dis[i]>dis[now]+cost[now][i])//這里將費用看成是長度,求源點到匯點的最小距離 { dis[i]=dis[now]+cost[now][i]; pre[i]=now; if(vis[i]==0) { p.push(i); vis[i]=1; cnt[i]++; if(cnt[i]>n) return 0; } } } } if(dis[endd]>=mx) return 0; return 1; } void bfs(int n)//頂點數 { memset(flow,0,sizeof(flow)); mx_flow=0; mn_cost=0; while(spfa()) { int f=mx; for(int i=endd;i!=st;i=pre[i]) f=min(f,cap[pre[i]][i]-flow[pre[i]][i]); for(int i=endd;i!=st;i=pre[i]) { flow[pre[i]][i]+=f; flow[i][pre[i]]-=f; } mn_cost+=dis[endd]*f;
mx_flow+=f; } }