最小費用最大流
通過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;
}
