題解
比較巧妙的費用流
修改流量與容量使得一張圖滿足流守恆與流量小於容量的條件
我們先來解決第二個問題:流量小於容量
發現只需要對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);
}