CF708DIncorrect Flow 最小费用可行流

Posted CQzhangyu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CF708DIncorrect Flow 最小费用可行流相关的知识,希望对你有一定的参考价值。

【CF708D】Incorrect Flow

题意:给你一个点数为n,边数为m的流网络,每条边有一个容量c和流量f,这个网络可能是不合法的。你可以花费1的代价使c或f减少或增加1,可以修改无限次。你不需要使流量最大,你只需要花费最少的代价把原图改造成一个合法的网络。

$n,m\le 100,c,f\le 10^6$

题解:我们用有上下界的费用流来解决这个问题。

对于一条边a->b,如果c>f,则我们从a到b连一条下界和上界都是f,费用为0的边;因为可以减少流量,所以连一条从b到a,容量为f,费用为1的边;因为可以增加流量,所以连一条从a到b,容量为c-f,费用为1的边;因为可以同时增加容量和流量,所以连一条从a到b,容量为inf,费用为2的边。

如果c<f,则我们先补充f-c个容量,直接将其加到答案中去,然后连一条从a到b,下界和上界都是f,费用为0的边;因为可以同时增加流量和容量,所以连一条从a到b,容量为inf,费用为2的边;因为可以减少流量,当减少量<f-c时,我们可以撤销一开始增加容量的操作,所以连一条从b到a,容量为f-c,费用为0的边;当减少量>f-c时,我们连一条从b到a,容量为c,费用为1的边。

跑最小费用可行流即可。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <queue>

using namespace std;
int n,m,cnt,ans,S,T;
int to[100010],nxt[100010],head[210],cost[100010],flow[100010],dis[210],pe[210],pv[210],inq[210],ml[210];
queue<int> q;
inline int rd()
{
	int ret=0,f=1;	char gc=getchar();
	while(gc<‘0‘||gc>‘9‘)	{if(gc==‘-‘)	f=-f;	gc=getchar();}
	while(gc>=‘0‘&&gc<=‘9‘)	ret=ret*10+(gc^‘0‘),gc=getchar();
	return ret*f;
}
int bfs()
{
	int i,u;
	memset(dis,0x3f,sizeof(dis));
	q.push(S),dis[S]=0;
	while(!q.empty())
	{
		u=q.front(),q.pop(),inq[u]=0;
		for(i=head[u];i!=-1;i=nxt[i])	if(flow[i]&&dis[to[i]]>dis[u]+cost[i])
		{
			dis[to[i]]=dis[u]+cost[i],pe[to[i]]=i,pv[to[i]]=u;
			if(!inq[to[i]])	q.push(to[i]),inq[to[i]]=1;
		}
	}
	return dis[T]<0x3f3f3f3f;
}
inline void add(int a,int b,int c,int d)
{
	to[cnt]=b,cost[cnt]=c,flow[cnt]=d,nxt[cnt]=head[a],head[a]=cnt++;
	to[cnt]=a,cost[cnt]=-c,flow[cnt]=0,nxt[cnt]=head[b],head[b]=cnt++;
}
int main()
{
	n=rd(),m=rd();
	int i,a,b,c,f;
	S=0,T=n+1;
	memset(head,-1,sizeof(head));
	for(i=1;i<=m;i++)
	{
		a=rd(),b=rd(),c=rd(),f=rd();
		if(c<f)
		{
			ans+=f-c;
			ml[a]+=f,ml[b]-=f;
			add(b,a,0,f-c),add(b,a,1,c),add(a,b,2,1<<30);
		}
		else
		{
			ml[a]+=f,ml[b]-=f;
			add(a,b,1,c-f),add(a,b,2,1<<30),add(b,a,1,f);
		}
	}
	add(n,1,0,1<<30);
	for(i=1;i<=n;i++)
	{
		if(ml[i]>=0)	add(i,T,0,ml[i]);
		else	add(S,i,0,-ml[i]);
	}
	while(bfs())
	{
		f=1<<30;
		for(i=T;i!=S;i=pv[i])	f=min(f,flow[pe[i]]);
		ans+=dis[T]*f;
		for(i=T;i!=S;i=pv[i])	flow[pe[i]]-=f,flow[pe[i]^1]+=f;
	}
	printf("%d",ans);
	return 0;
}

以上是关于CF708DIncorrect Flow 最小费用可行流的主要内容,如果未能解决你的问题,请参考以下文章

最小费用最大流 模板

最小费用最大流模板

ZOJ 3792 Romantic Value 最小割(最小费用下最小边数)

最小费用流模板

CF708A Letters Cyclic Shift 模拟

CF708B Recover the String 构造