zoukankan      html  css  js  c++  java
  • 网络流之费用流

    发现我真的是比较玄学,寒假主学最大流的时候忘记搞一下费用流了。然后现在来补。

    其实如果理解了最大流的思想和算法的话还是很好写的。毕竟就是把EK的BFS改成SPFA

    关于费用流的定义,我们在一般的网络流的定义上给每条边加上一个边权,边权的意义就是流量的单价(也就是每1单位的流经过这条边,那么就要边权的费用)。

    所以在一个网络图中最大流可能有挺多个,但是在最大流中肯定有一种流法在满足最大流的情况下保证流量最小

    这就是我们重点要讲的最小费用最大流(MCMF)

    首先在讲这个算法之前,你必须掌握最大流的相关东西,可以参照网络流之最大流

    然后在最大流中我们不是提到用增广路来求最大流吗,那么用什么来找呢?

    好吧就是神奇的BFS,BFS的遍历可以保证在残量网络中找到一个流

    可是这个方法只保证了流量最大,可能会出现让总费用大的路径代替总费用小的路径。

    那还不很伤?其实也很简单。

    我们只需要把BFS改成SFPA即可,而跑最短路的时候就是求费用的最短路。

    那么就可以简单地用EK求MCMF了。

    不过这里为什么只用SPFA,DJ不是很稳定吗?

    其实DJ也是可以的,但是由于网络流的基本性质——反向边。这样费用就可能为负,就无法用DJ求解了。

    不过这个其实是有办法避免的,不过技巧性太强,不予讨论。

    然后给出板子题Luogu P3381 【模板】最小费用最大流的CODE

    #include<cstdio>
    #include<cstring>
    #include<queue>
    using namespace std;
    const int N=5005,M=100005;
    queue <int> q;
    struct edge
    {
        int to,next,c,f;
    }e[M];
    int head[N],last[N],pre[N],dis[N],cap[N],cnt=-1,n,m,x,y,c,f,s,t;
    bool vis[N];
    inline char tc(void)
    {
        static char fl[100000],*A=fl,*B=fl;
        return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
    }
    inline void read(int &x)
    {
        x=0; char ch=tc();
        while (ch<'0'||ch>'9') ch=tc();
        while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=tc();
    }
    inline void add(int x,int y,int c,int f)
    {
        e[++cnt].to=y; e[cnt].c=c; e[cnt].f=f; e[cnt].next=head[x]; head[x]=cnt;
    }
    inline int min(int a,int b)
    {
        return a<b?a:b;
    }
    inline bool SPFA(void)
    {
        memset(pre,-1,sizeof(pre));
        memset(dis,63,sizeof(dis));
        memset(cap,63,sizeof(cap));
        memset(vis,0,sizeof(vis));
        while (!q.empty()) q.pop();
        q.push(s); dis[s]=0; vis[s]=1;
        while (!q.empty())
        {
            int now=q.front(); q.pop(); vis[now]=0;
            for (register int i=head[now];i!=-1;i=e[i].next)
            if (e[i].c&&dis[e[i].to]>dis[now]+e[i].f)
            {
                dis[e[i].to]=dis[now]+e[i].f;
                cap[e[i].to]=min(cap[now],e[i].c);
                pre[e[i].to]=now; last[e[i].to]=i;
                if (!vis[e[i].to])
                {
                    vis[e[i].to]=1;
                    q.push(e[i].to);
                }
            }
        }
        return pre[t]^-1;
    }
    inline void MCMF(void)
    {
        int tot=0,res=0;
        while (SPFA())
        {
            res+=cap[t]; tot+=cap[t]*dis[t];
            int now=t;
            while (now!=s)
            {
                e[last[now]].c-=cap[t];
                e[last[now]^1].c+=cap[t];
                now=pre[now];
            }
        }
        printf("%d %d",res,tot);
    }
    int main()
    {
        //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
        register int i; read(n); read(m); read(s); read(t);
        memset(e,-1,sizeof(e));
        memset(head,-1,sizeof(head));
        for (i=1;i<=m;++i)
        {
            read(x); read(y); read(c); read(f);
            add(x,y,c,f); add(y,x,0,-f);
        }
        MCMF(); return 0;
    }
    
  • 相关阅读:
    Javascript实现局部刷新
    Javascript模块化开发-轻巧自制
    javascript面向对象实例
    Javascript兼容和CSS兼容总结
    隐藏关机按钮
    数组排序
    常用数组获取最新和第一个元素值
    php 操作redis 以及几个常用命令
    git 常用命令
    JSON.parse和JSON.stringify的区别
  • 原文地址:https://www.cnblogs.com/cjjsb/p/9182547.html
Copyright © 2011-2022 走看看