dijkstra因为不能处理负权最短路,所以不能用来费用流的增广。

但是如果我们利用Johnson的思想,加一个势函数,把负变正,就能跑费用流了。

维护势函数也很简单,每次跑完dijkstra之后,对于最短路非INF的点,令势函数加上最短路即可。

dijkstra跑增广时直接把 w[i] -> w[i] + h[u] - h[v] ,对于u到v的边。


三种费用流比较:

EK+spfa:简单好写,速度慢。
EK+势函数的dijkstra:对于边多的情况很快。
ZKW:稠密图+流量大+费用小的情况比较快。


代码:

#pragma GCC optimize(2)
#include<bits/stdc++.h>
//#define int long long
using namespace std;
const int inf=0x3f3f3f3f;
const int N=5010,M=2e5+10;
int n,m,s,t,h[N],d[N],v[N],e[N],res,maxflow;
int head[N],nex[M],to[M],w[M],flow[M],tot=1;
inline void ade(int a,int b,int c,int d){
	to[++tot]=b; nex[tot]=head[a]; w[tot]=d; flow[tot]=c; head[a]=tot;
}
inline void add(int a,int b,int c,int d){ade(a,b,c,d);	ade(b,a,0,-d);}
inline int dijkstra(){
	memset(d,0x3f,sizeof d);	d[s]=0;	int vis[N]={0};
	priority_queue<pair<int,int> > q;	q.push({0,s});
	while(q.size()){
		int u=q.top().second;	q.pop();
		if(vis[u])	continue;	vis[u]=1;
		for(int i=head[u];i;i=nex[i]){
			if(flow[i]&&d[to[i]]>d[u]+w[i]+h[u]-h[to[i]]){
				d[to[i]]=d[u]+w[i]+h[u]-h[to[i]];
				v[to[i]]=u; e[to[i]]=i;	q.push({-d[to[i]],to[i]});
			}
		}
	}
	for(int i=1;i<=n;i++)	if(d[i]<inf)	h[i]+=d[i];
	return d[t]<inf;
}
void EK(){
	while(dijkstra()){
		int mi=inf;
		for(int i=t;i!=s;i=v[i])	mi=min(mi,flow[e[i]]);
		for(int i=t;i!=s;i=v[i])	flow[e[i]]-=mi,flow[e[i]^1]+=mi;
		res+=h[t]*mi,maxflow+=mi;
	}
}
signed main(){
	cin>>n>>m>>s>>t;
	for(int i=1,a,b,c,d;i<=m;i++)	scanf("%d %d %d %d",&a,&b,&c,&d),add(a,b,c,d);
	EK();	cout<<maxflow<<' '<<res<<endl;
	return 0;
}