基于 Dijkstra 的费用流算法
众所周知,Dijkstra 不可以处理有负权的图,若我们在跑 Dijkstra 前,对每条边的权值做一些处理,就可以使用 Dijkstra 了,单轮的时间复杂度降到
设一条边
考虑给每个点一个权值
不妨设
由于
考虑一次增广完后,对于满足
由于一般一开始都没有负权边,可以直接跑 Dijkstra,即不用求解
code:(【模板】最小费用最大流)
#include<bits/stdc++.h>
#define ull unsigned long long
#define int long long
#define p_b push_back
#define m_p make_pair
#define pii pair<int,int>
#define fi first
#define se second
#define ls k<<1
#define rs k<<1|1
#define mid ((l+r)>>1)
#define gcd __gcd
#define lowbit(x) (x&(-x))
using namespace std;
int rd(){
int x=0,f=1; char ch=getchar();
for(;ch<'0'||ch>'9';ch=getchar())if (ch=='-') f=-1;
for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<1)+(x<<3)+(ch^48);
return x*f;
}
void write(int x){
if(x>9) write(x/10);
putchar('0'+x%10);
}
const int N=5e3+5,INF=0x3f3f3f3f3f3f3f3f;
struct edge{
int to,nt,c,w;
}a[N<<5];
int head[N],at=1,h[N],d[N],vis[N],n,s,t,pre[N],m;
void add(int u,int v,int c,int w){
a[++at]=(edge){v,head[u],c,w};
head[u]=at;
}
void link(int u,int v,int c,int w){
add(u,v,c,w);
add(v,u,0,-w);
}
void dij(){
for(int i=1;i<=n;i++)d[i]=INF;
int flow=0,cost=0;
while(1){
priority_queue<pii> q;
d[s]=0;q.push(m_p(0,s));
while(!q.empty()){
int u=q.top().se;q.pop();
if(vis[u])continue;vis[u]=1;
for(int i=head[u];i;i=a[i].nt){
int v=a[i].to,c=a[i].c,w=a[i].w+h[u]-h[v];
if(c&&d[v]>d[u]+w){
d[v]=d[u]+w,pre[v]=i;
q.push(m_p(-d[v],v));
}
}
}
if(d[t]==INF) break;
int res=INF;
for(int i=t;i!=s;i=a[pre[i]^1].to) res=min(res,a[pre[i]].c);
flow+=res;
for(int i=t;i!=s;i=a[pre[i]^1].to) a[pre[i]].c-=res,a[pre[i]^1].c+=res,cost+=res*a[pre[i]].w;
for(int i=1;i<=n;i++) h[i]=min(INF,h[i]+d[i]),d[i]=INF,vis[i]=0;
}
printf("%lld %lld\n",flow,cost);
}
signed main(){
n=rd(),m=rd(),s=rd(),t=rd();
for(int i=1;i<=m;i++){
int u=rd(),v=rd(),c=rd(),w=rd();
link(u,v,c,w);
}
dij();
return 0;
}