最小费用最大流?

  最小费用最大流就是在最大流的基础上,给每条边一个单位花费,要在保证是最大流的情况下找出最小费用。这里的单位花费就是这条边的每一单位流量的花费。

解法:

  先看这里的花费,假设第i条边的花费是wi,那么假设找到 了一条增广路(与最大流中的意义相同),且这条增广路的流量为x,那么这条增广路第i条边的花费就是x*wi,将x提出来,就可以得到整条增广路的花费就是,这i条边的单位花费之和*流量x。因为最大流是一定的了(因为要先保证找到的是最大流),那么只要这条增广路上的总的单位花费最小就可以了。这样就可以用spfa来跑最短路代替最大流中的dfs找增广路,这样就可以缩小费用了。其他的思路基本上与最大流相同,用增广路上的每条边减去这条路上的最小边,用其反边加上这条边。

  然后就是将总的花费记录下来,按照上面的思路,在用spfa找增广路时,会用一个dis数组来记录下从s(起点)开始到每个点的最短路,然后用dis[t(终点)]*x(流量),就可以得到这条增广路的花费,然后将其累加起来,就找到了总花费。最后print就可以了、、、、

  具体实现的时候还要考虑到找到增广路之后怎么对它在进行操作(也就是找最小流量和增流),可以在spfa的时候用一个faz数组记录下每个点最优方案下的父亲,然后再次遍历的时候就从t(终点)开始倒着找爸爸。

一道例题:luogu3381

 

代码:

#include<queue>
#include<cstring>
#include<cstdio>
#include<deque>
#include<iostream>
using namespace std;
const int N=5010*2,M=50010*2;
deque<int>q;
struct node
{
    int v,w,nxt,cost;
}edg[M];
int ejs,n,m,s,t,head[N],ans_cost,ans_w,dis[N],faz[N],map[N],inqu[N];
void add(int u,int v,int w,int cost)
{
    edg[++ejs].v=v;
    edg[ejs].w=w;
    edg[ejs].cost=cost;
    edg[ejs].nxt=head[u];
    head[u]=ejs;
}
int change(int x)
{
    if(x%2) return x+1;
    return x-1;
}
bool spfa()
{
    memset(faz,0,sizeof(faz));
    memset(inqu,0,sizeof(inqu));
    memset(dis,0x3f,sizeof(dis));
    memset(map,0,sizeof(map));
    while(!q.empty()) q.pop_front();
    q.push_back(s);
    dis[s]=0;
    inqu[s]=1;
    while(!q.empty())
    {
        int u=q.front();
        inqu[u]=0;
        q.pop_front();
        for(int i=head[u];i;i=edg[i].nxt)
        {
            int v=edg[i].v; 
            if(dis[v]>dis[u]+edg[i].cost&&edg[i].w>0)
            {
                dis[v]=dis[u]+edg[i].cost;
                faz[v]=u;
                map[v]=i;
                if(!inqu[v])
                {
                    
                    if(!q.empty())
                    {
                        int k=q.front();
                        if(dis[v]<dis[k])
                        q.push_front(v);
                        else q.push_back(v); 
                    }
                    else q.push_back(v); 
                    inqu[v]=1; 
                }
            }
        }
     }
     if(faz[t]) return 1;
     else return 0;
}
void work()
{
    while(spfa())
    {
        int x=0x7fffffff,now=t;
        while(now!=s)
        {
            x=min(x,edg[map[now]].w);
            now=faz[now];
        }
        now=t;
        ans_w+=x;
        ans_cost+=dis[t]*x;
        while(now!=s)
        {
            edg[map[now]].w-=x;
            edg[change(map[now])].w+=x;    
            now=faz[now];
        }
    }
}
int main()
{
    scanf("%d%d%d%d",&n,&m,&s,&t);
    for(int i=1,x,y,a,b;i<=m;++i)
    {
        scanf("%d%d%d%d",&x,&y,&a,&b);
        add(x,y,a,b);
        add(y,x,0,-b);
    }
    work();
    printf("%d %d",ans_w,ans_cost);
    return 0;
}
luogu3381

PS:这个代码中用到了双端队列deque来优化spfa