最小费用最大流

 min{sum(cost(i, j)*f(i,j) | (i, j)属于方案f中的边, f(i,j)为 边(i,j)上的流量, f为某一个最大流方案}。此即为最小费用最大流

算法思想

 
    由于最大流量有限,每执行一次循环流量都会增加,因此该算法肯定会结束,且同时流量也必定会达到网络的最大流量;同时由于每次都是增加的最小的花费,即当前的最小花费是所有到达当前流量flow时的花费最小值,因此最后的总花费最小。

求解步骤

 
(4)重复(1)--(3)直到无法找到从源点到达汇点的路径。

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

 

相关文章: