题解
比较巧妙的费用流
修改流量与容量使得一张图满足流守恒与流量小于容量的条件
我们先来解决第二个问题:流量小于容量
发现只需要对c<f的边加上f-c的代价就可以了(直接修改容量c)
再来解决流守恒的条件
我们利用上下界网络流固定原图每条边的流量
现在只需要加上一些额外的修改边,再跑一遍上下界网络流,这个图就可以自动满足流守恒的条件
对于一条边u,v,c,f
当c<f时
由于我们预支了f-c的代价,使得c'=f
如果我们想要在现在减小流量,我们也可以回退容量的代价,使得他们两个抵消
但是如果流量减小得比以前的容量还小,那么我们预支的费用就不够抵消减小流量的代价了
所以我们还要再支出费用,单价为1
如果我们想要增大流量,我们现在的c'=f
由于要维持流量小于容量的条件,我们加一个单位的流量就还得加一个单位的容量,单价为2
用网络流的语言来翻译上面的话就是(adde(u,v,l,r,cost)表示加一条u->v上下界为[l,r],费用为cost的边)
adde(v,u,0,f-c,0) (减小流量相当于在退流,所以是反向连边)
adde(v,u,0,c,1)
adde(u,v,0,inf,2)
当c>=f时
此时不用预支费用,但是修改流量还是有花费的
我们最多可以减小f个流量,费用为1(adde(v,u,0,f,1))
可以在小于等于c的范围内增加流量,费用也为1(adde(u,v,0,c-f,1))
如果超出了c的范围,此时我们还得同时修改容量来满足条件2,费用为2(adde(u,v,0,inf,2))
最后跑一遍上下界最小费用可行流就可以了
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
#define N 505
#define M 100005
#define LL long long
int fir[N],cur[N],to[M],nxt[M],cap[M],cst[M],cnt;
void addedge(int a,int b,int c,int d)
{
to[++cnt]=b;nxt[cnt]=fir[a];fir[a]=cnt;cap[cnt]=c;cst[cnt]=d;
to[++cnt]=a;nxt[cnt]=fir[b];fir[b]=cnt;cap[cnt]=0;cst[cnt]=-d;
}
int S,T,SS,TT,sz;
int deg[N];
LL mic,flow,pc;
void adde(int a,int b,int l,int r,int d)
{
if(l)deg[b]+=l,deg[a]-=l,pc+=1ll*d*l;
addedge(a,b,r-l,d);
}
int dis[N];bool vis[N];
queue<int> q;bool inq[N];
const int INF=0x3f3f3f3f;
bool spfa()
{
for(int i=1;i<=sz;i++)dis[i]=INF;
q.push(TT);inq[TT]=1;dis[TT]=0;
while(!q.empty()){
int u=q.front();q.pop();inq[u]=0;
for(int v,w,p=fir[u];p;p=nxt[p]){
v=to[p];w=cst[p^1];
if(cap[p^1]>0&&dis[v]>dis[u]+w){
dis[v]=dis[u]+w;
if(!inq[v])q.push(v),inq[v]=1;
}
}
}
return dis[SS]!=INF;
}
LL sap(int u,LL aug)
{
if(u==TT){mic+=aug*dis[SS];return aug;}
int tmp,ret=0;
vis[u]=1;
for(int v,&p=cur[u];p;p=nxt[p]){
v=to[p];
if(!vis[v]&&cap[p]>0&&dis[u]==dis[v]+cst[p]){
tmp=sap(v,min(aug,1ll*cap[p]));
cap[p]-=tmp;aug-=tmp;
cap[p^1]+=tmp;ret+=tmp;
if(aug==0)break;
}
}
vis[u]=0;
return ret;
}
void micflow()
{
while(spfa()){
for(int i=1;i<=sz;i++)cur[i]=fir[i];
flow+=sap(SS,INF);
}
}
int main()
{
cnt=1;
int n,m,i,u,v,c,f;
scanf("%d%d",&n,&m);
S=1;T=n;SS=n+1;TT=n+2;
sz=TT;
for(i=1;i<=m;i++){
scanf("%d%d%d%d",&u,&v,&c,&f);
adde(u,v,f,f,0);
if(c<f){
mic+=f-c;
adde(v,u,0,f-c,0);
adde(v,u,0,c,1);
adde(u,v,0,INF,2);
}
else{
adde(u,v,0,c-f,1);
adde(u,v,0,INF,2);
adde(v,u,0,f,1);
}
}
adde(T,S,0,INF,0);
for(i=1;i<=n;i++){
if(deg[i]>0)addedge(SS,i,deg[i],0);
else if(deg[i]<0)addedge(i,TT,-deg[i],0);
}
micflow();
printf("%lld\n",mic);
}