题解 P7173 【有负圈的费用流】
增广路算法由于实现中存在最短路算法,无法处理存在费用负圈的最小费用流问题。
消圈算法本身就有消除负圈的过程,但由于效率低下,在 OI 中并不实用。
我们考虑利用上下界网络流的技术来解决负圈的问题。
对于网络中的负费用边
满流直接用上下界费用流的技术解决,跑一个有源汇上下界最小费用最大流即可。
代码如下:
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=200,M=10000,INF=(1<<30)-1;
int n,m,st0,td0,df[N+9];
int ans[2];
struct side{
int y,next,f,v;
}e[M*2+N*2+9];
int lin[N+9],cs;
void Ins(int x,int y,int f,int v){e[++cs].y=y;e[cs].f=f;e[cs].v=v;e[cs].next=lin[x];lin[x]=cs;}
void Ins_flow(int x,int y,int f,int v){Ins(x,y,f,v);Ins(y,x,0,-v);}
void into(){
scanf("%d%d%d%d",&n,&m,&st0,&td0);
cs=1;
for (int i=1;i<=m;++i){
int x,y,f,v;
scanf("%d%d%d%d",&x,&y,&f,&v);
if (v>=0) Ins_flow(x,y,f,v);
else{
df[x]-=f;df[y]+=f;ans[1]+=f*v;
Ins_flow(y,x,f,-v);
}
}
}
int st,td,cn;
void Get_graph(){
st=n+1;td=cn=n+2;
for (int i=1;i<=cn-2;++i){
if (!df[i]) continue;
df[i]>0?Ins_flow(st,i,df[i],0):Ins_flow(i,td,-df[i],0);
}
Ins_flow(td0,st0,INF,0);
}
int flow,value;
int dis[N+9],vis[N+9],pre[N+9],f[N+9];
queue<int>q;
bool Spfa(int st,int td){
for (int i=1;i<=cn;++i) dis[i]=INF,vis[i]=0;
dis[st]=0;vis[st]=1;f[st]=INF;q.push(st);
for (;!q.empty();){
int t=q.front();q.pop();
vis[t]=0;
for (int i=lin[t];i;i=e[i].next)
if (e[i].f&&dis[t]+e[i].v<dis[e[i].y]){
dis[e[i].y]=dis[t]+e[i].v;
pre[e[i].y]=i;
f[e[i].y]=min(f[t],e[i].f);
if (!vis[e[i].y]) vis[e[i].y]=1,q.push(e[i].y);
}
}
return dis[td]^INF;
}
void Max_flow(int st,int td){
flow=0;value=0;
for (;Spfa(st,td);flow+=f[td],value+=f[td]*dis[td])
for (int k=td;k^st;k=e[pre[k]^1].y) e[pre[k]].f-=f[td],e[pre[k]^1].f+=f[td];
}
void Get_ans(){
Max_flow(st,td);
ans[1]+=value;
Max_flow(st0,td0);
ans[0]=flow;ans[1]+=value;
}
void work(){
Get_graph();
Get_ans();
}
void outo(){
printf("%d %d\n",ans[0],ans[1]);
}
int main(){
into();
work();
outo();
return 0;
}