最大/最小费用流(板子整理)

  • Post author:
  • Post category:其他


板子(以院赛费用流裸题为例)

一般把超级源点s赋0,超级汇点t赋1+所有点的个数num

连边时调用add2函数,正向边流量满值且费用为正,负向边流量为0且费用为负

开点时点和边权的数组别开小了,考虑最极限的情况来开数组

这个板子是从汇点反向spfa的,引入了势的概念,加入了spfa的双端队列优化

调用maxflow(源点,汇点,费用值)的函数,函数返回值为最大流

最小费用最大流,正费用建图跑

最大费用最大流,负费用建图,

求出最小的负的费用,其绝对值就是最大的正费用


板子是针对有向边的情形,


无向边的图,需要拆分成两条有向边,即两次加边。

代码

#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
const int maxn=110;//点的数量 应为 两边点数总和+2
const int maxm=50005;//2*maxn*maxn+4*maxn 源点出maxn条 汇点入maxn条 中间点两两连 
const ll INF=1e18; 
struct edge
{
   int u,v;
   ll cos,nex,w;
}e[maxm];
ll vis[2*maxn],dis[2*maxn];
ll l[2*maxn],r[2*maxn],cost[2*maxn][2*maxn];
int head[2*maxn],cnt;
void add(int u,int v,ll w,ll cos)
{
	e[cnt].v=v;
	e[cnt].w=w;
	e[cnt].cos=cos;
	e[cnt].nex=head[u];
	head[u]=cnt++;
}
void add2(int u,int v,ll w,ll cos)
{
	add(u,v,w,cos);
	add(v,u,0,-cos);
}
bool spfa(int s,int t)
{
	for(int i=0;i<2*maxn;++i)
	dis[i]=INF;
	memset(vis,0,sizeof vis);
	vis[t]=1;
	dis[t]=0;
	deque<int>q;
	q.push_back(t);
	while(!q.empty())
	{
		int u=q.front();
		vis[u]=0;
		q.pop_front();
		for(int i=head[u];~i;i=e[i].nex)
		{
			int v=e[i].v;
			if(e[i^1].w>0&&dis[v]>dis[u]-e[i].cos)
			{
				dis[v]=dis[u]-e[i].cos;
				if(!vis[v])
				{
					vis[v]=1;
					if(!q.empty()&&dis[v]<dis[q.front()])
					q.push_front(v);
					else q.push_back(v);
				}
			}
		}
	}
	return dis[s]<INF;
}
ll dfs(int u,int t,ll low,ll &tot)
{
	vis[u]=1;
	if(u==t)return low;
	ll ans=0;
	for(int i=head[u];~i;i=e[i].nex)
	{
		int v=e[i].v;
		if(!vis[v]&&e[i].w&&dis[v]==dis[u]-e[i].cos)
		{
			ll now=dfs(v,t,min(low,e[i].w),tot);
			if(now>0)
			{
				tot+=e[i].cos*now;
				low-=now;
				ans+=now;
				e[i].w-=now;
				e[i^1].w+=now;
				if(low==0)break;
			}
		}
	}
	return ans;
}
ll max_flow(int s,int t,ll &tot)//tot返回费用 函数返回值为最大流 
{
	ll ans=0;
	while(spfa(s,t))
	{
		vis[t]=1;
		while(vis[t])
		{
			memset(vis,0,sizeof vis);
			ans+=dfs(s,t,INF,tot);
		}
	}
	return ans;
} 
void init()
{
	cnt=0;
	for(int i=0;i<2*maxn;++i)
	dis[i]=INF;
	memset(head,-1,sizeof head);
	memset(vis,0,sizeof vis);
}
int main()
{
	ll tot;//总费用 
	int n,m,s,t;
    tot=0;
	init();
    scanf("%d%d",&m,&n);
    //超级源 超级汇
    s=0;t=m+n+1; 
    //左供货 右需求
    for(int i=1;i<=m;++i)
    {
    	scanf("%lld",&l[i]);
	    add2(s,i,l[i],0);//超级源点 
    }
    for(int i=1;i<=n;++i)
	{
	    scanf("%lld",&r[i]);
        add2(m+i,t,r[i],0);//超级汇点
	} 
    for(int i=1;i<=m;++i) 
    {
    	for(int j=1;j<=n;++j)
    	{
    		scanf("%lld",&cost[i][j]);
    		add2(i,m+j,l[i],cost[i][j]);//l[i]的流量 dis的费用 
    	}
    }
    max_flow(s,t,tot);
    printf("%lld\n",tot);
    tot=0;
    init();
    for(int i=1;i<=m;++i)
    {
	    add2(s,i,l[i],0);//超级源点 
    }
    for(int i=1;i<=n;++i)
	{
        add2(m+i,t,r[i],0);//超级汇点
	} 
    for(int i=1;i<=m;++i) 
    {
    	for(int j=1;j<=n;++j)
    	{
    		add2(i,m+j,l[i],-cost[i][j]);//l[i]的流量 dis的费用 
    	}
    }
    max_flow(s,t,tot);
    printf("%lld\n",-tot);
   return 0;
}



版权声明:本文为Code92007原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。