最小费用最大流 学习笔记

板子题

题目传送门
给出一个网络,每条边有一个流量上限和单位流量的费用,求最大流以及此时最小费用。

算法解析

我们发现原来的 Dinic 和 EK 解决不了这个问题,因为它们不能求出最小费用。
我们发现,如果在增广的时候选择单位流量价格最小的一条路径增广就可以做到求出最小费用,同时可以求出最大流。换句话说我们只要把 bfs 改成求最短路了,可以用 SPFA 或者是 Dijkstra,当然有负权边的时候我们要对边进行一些处理才能用 Dijkstra。

这里使用 SPFA + EK 来实现。
代码:

#include<queue>
#include<cstdio>
#include<cstring>
#define maxn 5039
#define maxm 100039
#define min(a,b) ((a)<(b)?(a):(b))
using namespace std;
//#define debug
typedef int Type;
inline Type read(){
	Type sum=0;
	int flag=0;
	char c=getchar();
	while((c<'0'||c>'9')&&c!='-') c=getchar();
	if(c=='-') c=getchar(),flag=1;
	while('0'<=c&&c<='9'){
		sum=(sum<<1)+(sum<<3)+(c^48);
		c=getchar();
	}
	if(flag) return -sum;
	return sum;
}
struct JTZ{
	int num,dis;
	bool operator > (const JTZ &x) const {
		return this->dis > x.dis;
	}
};
queue<int> q,E;
int head[maxn],to[maxm],nex[maxm],w[maxm],c[maxm],kkk=1;
int n,m,s,t,u,v,x,y;
#define add(x,y,z,co) nex[++kkk]=head[x];\
head[x]=kkk; to[kkk]=y;\
w[kkk]=z; c[kkk]=co;
int flow[maxn],dis[maxn],edge[maxn],pre[maxn],vis[maxn];
int spfa(){
	memset(dis,0x7f,sizeof(dis)); memset(vis,0,sizeof(vis));
	memset(flow,0x7f,sizeof(flow)); q=E; q.push(s);
	vis[s]=1; pre[t]=-1; dis[s]=0; q.push(s);
	while(!q.empty()){
		int cur=q.front(); q.pop(); vis[cur]=0;
		for(int i=head[cur];i;i=nex[i])
			if(w[i]>0&&dis[to[i]]>dis[cur]+c[i]){
				dis[to[i]]=dis[cur]+c[i]; pre[to[i]]=cur; edge[to[i]]=i;
				flow[to[i]]=min(flow[cur],w[i]);
				if(!vis[to[i]]) vis[to[i]]=1,q.push(to[i]);
			}
	}
	return ~pre[t];
}
void MCMF(){
	int ans=0,ret=0,x;
	while(spfa()){
		x=t; ans+=flow[t]; ret+=flow[t]*dis[t];
		while(x!=s){
			w[edge[x]]-=flow[t];
			w[edge[x]^1]+=flow[t];
			x=pre[x];
		}
	}
	printf("%d %d",ans,ret);
	return;
}
int main(){
	//freopen("1.in","r",stdin);
	//freopen(".out","w",stdout);
    n=read(); m=read(); s=read(); t=read();
    for(int i=1;i<=m;i++){
    	u=read(); v=read(); x=read(); y=read();
    	add(u,v,x,y) add(v,u,0,-y)
	}
	MCMF();
	return 0;
}

posted @ 2021-08-25 13:51  jiangtaizhe001  阅读(53)  评论(0编辑  收藏  举报