CF708D Incorrect Flow(上下界最小費用可行流)

 

 

題解

比較巧妙的費用流

修改流量與容量使得一張圖滿足流守恆與流量小於容量的條件

我們先來解決第二個問題:流量小於容量

發現只需要對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);
}

 

 

 

 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章