最小費用最大流
通過EK,Dinic,ISAP算法可以得到網絡流圖中的最大流,一個網絡流圖中最大流的流量max_flow是唯一的,但是達到最大流量max_flow時每條邊上的流量分配f是不唯一的。
如果給網絡流圖中的每條邊都設置一個費用cost,表示單位流量流經該邊時會導致花費cost。那么在這些流量均為max_flow的流量分配f中,存在一個流量總花費最小的最大流方案。
即 min{sum(cost(i, j)*f(i,j) | (i, j)屬於方案f中的邊, f(i,j)為 邊(i,j)上的流量, f為某一個最大流方案}。此即為最小費用最大流
。
算法思想
采用貪心的思想,每次找到一條從源點到達匯點的路徑,增加流量,且該條路徑滿足使得增加的流量的花費最小,直到無法找到一條從源點到達匯點的路徑,算法結束。
由於最大流量有限,每執行一次循環流量都會增加,因此該算法肯定會結束,且同時流量也必定會達到網絡的最大流量;同時由於每次都是增加的最小的花費,即當前的最小花費是所有到達當前流量flow時的花費最小值,因此最后的總花費最小。
求解步驟
(1)找到一條從源點到達匯點的“距離最短”的路徑,“距離”使用該路徑上的邊的單位費用之和來衡量。
(2)然后找出這條路徑上的邊的容量的最小值f,則當前最大流max_flow擴充f,同時當前最小費用min_cost擴充 f*min_dist(s,t)。
(3)將這條路徑上的每條正向邊的容量都減少f,每條反向邊的容量都增加f。
(4)重復(1)--(3)直到無法找到從源點到達匯點的路徑。
需要注意幾點:
1、注意超級源點和超級終點的建立。
2、初始化時,正向邊的單位流量費用為cost[u][v],那么反向邊的單位流量費用就為-cost[u][v]。因為回流費用減少。
3、費用cost數組和容量cap數組每次都要初始化為0。
求解從源點到匯點的“最短”路徑時,由於網絡中存在負權邊,因此使用SPFA來實現。
實現
#define INFINITE 1 << 26 #define MAX_NODE 1005 #define MAX_EDGE_NUM 40005 struct Edge{ int to; int vol; int cost; int next; }; Edge gEdges[MAX_EDGE_NUM]; int gHead[MAX_NODE]; int gPre[MAX_NODE]; int gPath[MAX_NODE]; int gDist[MAX_NODE]; int gEdgeCount; void InsertEdge(int u, int v, int vol, int cost){ gEdges[gEdgeCount].to = v; gEdges[gEdgeCount].vol = vol; gEdges[gEdgeCount].cost = cost; gEdges[gEdgeCount].next = gHead[u]; gHead[u] = gEdgeCount++; gEdges[gEdgeCount].to = u; gEdges[gEdgeCount].vol = 0; //vol為0,表示開始時候,該邊的反向不通 gEdges[gEdgeCount].cost = -cost; //cost 為正向邊的cost相反數,這是為了 gEdges[gEdgeCount].next = gHead[v]; gHead[v] = gEdgeCount++; } //假設圖中不存在負權和環,SPFA算法找到最短路徑/從源點s到終點t所經過邊的cost之和最小的路徑 bool Spfa(int s, int t){ memset(gPre, -1, sizeof(gPre)); memset(gDist, 0x7F, sizeof(gDist)); gDist[s] = 0; queue<int> Q; Q.push(s); while (!Q.empty()){//由於不存在負權和環,因此一定會結束 int u = Q.front(); Q.pop(); for (int e = gHead[u]; e != -1; e = gEdges[e].next){ int v = gEdges[e].to; if (gEdges[e].vol > 0 && gDist[u] + gEdges[e].cost < gDist[v]){ gDist[v] = gDist[u] + gEdges[e].cost; gPre[v] = u; //前一個點 gPath[v] = e;//該點連接的前一個邊 Q.push(v); } } } if (gPre[t] == -1) //若終點t沒有設置pre,說明不存在到達終點t的路徑 return false; return true; } int MinCostFlow(int s, int t){ int cost = 0; int flow = 0; while (Spfa(s, t)){ int f = INFINITE; for (int u = t; u != s; u = gPre[u]){ if (gEdges[gPath[u]].vol < f) f = gEdges[gPath[u]].vol; } flow += f; cost += gDist[t] * f; for (int u = t; u != s; u = gPre[u]){ gEdges[gPath[u]].vol -= f; //正向邊容量減少 gEdges[gPath[u]^1].vol += f; //反向邊容量增加 } } return cost; }