最小費用最大流


最小費用最大流

    通過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;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM