费用流

费用流

给出一个网络图,以及其源点和汇点,每条边已知其最大流量和单位流量费用,求出其网络最大流和在最大流情况下的最小费用。

注意,这里的费用意思是说每流一个单位的流量所消耗的费用。只需将Dinic中的bfs+dfs改为spfa即可。将费用看成距离,每次找到的最短路就是最小费用可行增广路。

#include <cstdio>
#include <cstring>
using namespace std;

const int maxn=5005, maxm=1e5+5, INF=1e9;
int n, m, src, dst, maxf, minc;
inline int min(int x, int y){ return x<y?x:y; }

struct Edge{
	int fr, to, nxt, f, v;  //f:容量 v:费用 
}e[maxm*2];
int cnte, fir[maxn];
void addedge(int x, int y, int flow, int val){
	Edge &ed=e[++cnte];
	ed.fr=x; ed.to=y; ed.nxt=fir[x]; ed.f=flow; 
	ed.v=val; fir[x]=cnte; }

int q[maxm], head, tail, dis[maxn], pre[maxn], flow[maxn], vis[maxn];  //pre表示上一条边 
bool spfa(){  //每次找出费用最小的增广路 
	head=tail=0; q[tail++]=src; int u, v; 
	memset(vis, 0, sizeof(vis)); 
	for (int i=1; i<=n; ++i) dis[i]=flow[i]=INF;
	pre[dst]=0; dis[src]=0;
	while (head<tail){
		u=q[head++]; vis[u]=0;
		for (int i=fir[u]; i; i=e[i].nxt){
			v=e[i].to; 
			if (e[i].f&&dis[u]+e[i].v<dis[v]){
				dis[v]=dis[u]+e[i].v; pre[v]=i;   
				flow[v]=min(flow[u], e[i].f);
				if (!vis[v]) q[tail++]=v; vis[v]=1;
			}
		}
	}
	return pre[dst];
}

int main(){
	scanf("%d%d%d%d", &n, &m, &src, &dst); int u, v, w, f;
	for (int i=0; i<m; ++i){
		scanf("%d%d%d%d", &u, &v, &w, &f);
		addedge(u, v, w, f); addedge(v, u, 0, -f);
	}
	while (spfa()){
		u=dst; maxf+=flow[dst];
		minc+=flow[dst]*dis[dst];  //相当于费用和乘以流量 
		while (u!=src){
			e[pre[u]].f-=flow[dst];
			e[((pre[u]-1)^1)+1].f+=flow[dst];
			u=e[pre[u]].fr;
		}
	}
	printf("%d %d
", maxf, minc);
	return 0;
}
原文地址:https://www.cnblogs.com/MyNameIsPc/p/9141995.html