最小费用最大流——EK+SPFA

终于把最小费用最大流学会了啊……
各种奇奇怪怪的解释我已经看多了,但在某些大佬的指点下,我终于会了。
原来是个好水的东西。

最小费用最大流是什么?

不可能不知道网络流吧?如果不知道,自行百度去……
费用流就是在每条边添加个费用,设你这条边的流量是 f f ,费用为w,则总费用为 fw f w
举个例子,就像是有许多点的一张图,有很多个管子相连,每个管子都有个容量,并且每流一流量就要花一些费用,问总费用最少是多少。


最小费用最大流怎么做?

首先要知道EK算法……
开玩笑的,其实根本不用,我还没打过普通的EK呢,就只是打过dinic和sap。
这个做法其实很简单:
1、用spfa从原点跑最短路,边权为费用(满流的边不用跑)。
2、将最短路径抽出,在上面找一个残余容量最小的。
3、这一路上的残余容量减少,反向弧的容量增加(反向弧的费用为负的正向的费用)。
4、回到第一步,继续做下去,直到从原点跑不到汇点。
这样就可以算出来了,这就是最普通的EK+spfa做法。
当然还有更好的,但我还不会……
时间复杂度我不知道,毕竟,网络流的时间总是很玄学啊……


代码

using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
int n,m,S,T;
struct EDGE
{
    int to,c,w;
    EDGE *las;
} e[100001];
int ne;
EDGE *last[5001];
#define rev(ei) (e+(int((ei)-e)^1))
int q[1000001];
bool inq[5001];
bool SPFA();
int dis[5001];
EDGE *pre[5001];
void flow(int&,int&);
int main()
{
    scanf("%d%d%d%d",&n,&m,&S,&T);
    for (int i=1;i<=m;++i)
    {
        int u,v,c,w;
        scanf("%d%d%d%d",&u,&v,&c,&w);
        e[ne]={v,c,w,last[u]};
        last[u]=e+ne;
        ++ne;
        e[ne]={u,0,-w,last[v]};
        last[v]=e+ne;
        ++ne;
    }
    int maxflow,mincost;
    flow(maxflow,mincost);
    printf("%d %d\n",maxflow,mincost);
    return 0;
}
bool SPFA()
{
    int h=-1,t=0;
    memset(dis,127,sizeof dis);
    dis[S]=0;
    pre[S]=NULL;
    q[0]=S;
    inq[S]=1;
    do
    {
        ++h;
        for (EDGE *ei=last[q[h]];ei;ei=ei->las)
            if (ei->c && dis[q[h]]+ei->w<dis[ei->to])
            {
                dis[ei->to]=dis[q[h]]+ei->w;
                pre[ei->to]=ei;
                if (!inq[ei->to])
                {
                    inq[ei->to]=1;
                    q[++t]=ei->to;
                }
            }
        inq[q[h]]=0;
    }
    while (h!=t);
    return dis[T]!=0x7f7f7f7f;
}
void flow(int &maxflow,int &mincost)
{
    maxflow=0;
    mincost=0;
    while (SPFA())
    {
        int minc=0x7f7f7f7f;
        for (EDGE *ei=pre[T];ei;ei=pre[rev(ei)->to])
            minc=min(minc,ei->c);
        maxflow+=minc;
        mincost+=dis[T]*minc;
        for (EDGE *ei=pre[T];ei;ei=pre[rev(ei)->to])
        {
            ei->c-=minc;
            rev(ei)->c+=minc;
        }
    }
}

这个代码我没有对过任何的标程,相信各位可以凭借自己的理解打出来,解析就不打了。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值