zoukankan      html  css  js  c++  java
  • 最小费用最大流——ZKW

    对于最小费用最大流,我们的通常做法是EK+SPFA。
    然而,卡常界大佬ZKW发明了一个求解最小费用最大流的方法,很强啊。
    在学ZKW费用流前,先说说KM算法。

    KM算法

    为啥要先提这个呢?因为ZKW费用流用了一个和它非常类似的做法。
    KM算法求的是二分图最大权完美匹配。
    在此,我来口胡一下(这个算法其实我并未打过,只懂思想)。
    和匈牙利算法差不多,区别在于标号。
    对于左右两边的点各自有个标号D,一开始,左边的标号设为最大的连出去的边的权值,右边的设为0。
    每次只走,满足Di+Dj=v的边。
    当发生冲突时,就将右边的点的标号全部加一,左边的点的标号全部减一,然后继续搞。
    这就是KM算法的大概思路。

    ZKW费用流

    到正题了。
    类似于KM算法,ZKW算法也有个标号。
    Dx=Dy+w时,才会走这条边。
    ZKW算法中,是类似于Dinic的多路增广,在每次增广后,就修改距离标号。
    求出距离标号的最小增加值,然后修改距离标号。
    当找不出增广路时,算法结束。

    代码

    using namespace std;
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <climits>
    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 maxflow,mincost;
    bool vis[5001];
    int dis[5001];
    int dfs(int,int);
    bool change();
    void flow();
    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;
        }
        flow();
        printf("%d %d
    ",maxflow,mincost);
        return 0;
    }
    int dfs(int x,int s)
    {
        if (x==T)
        {
            maxflow+=s;
            mincost+=dis[S]*s;
            return s;
        }
        vis[x]=1;
        int have=0;
        for (EDGE *ei=last[x];ei;ei=ei->las)
            if (!vis[ei->to] && ei->c && dis[ei->to]+ei->w==dis[x])
            {
                int t=dfs(ei->to,min(s-have,ei->c));
                ei->c-=t;
                rev(ei)->c+=t;
                have+=t;
            }
        return have;
    }
    bool change()
    {
        int d=INT_MAX;
        for (int i=1;i<=n;++i)
            if (vis[i])
                for (EDGE *ei=last[i];ei;ei=ei->las)
                    if (!vis[ei->to] && ei->c)
                        d=min(d,dis[ei->to]-dis[i]+ei->w);//找出最小增加值
        if (d==INT_MAX)
            return 0;
        for (int i=1;i<=n;++i)
            if (vis[i])
                dis[i]+=d;//更改距离标号
        return 1;
    }
    void flow()
    {
        maxflow=0;
        mincost=0;
        do
            do
                memset(vis,0,sizeof vis);
            while (dfs(S,INT_MAX));
        while (change());   
    }
  • 相关阅读:
    TransactSQL语言 学习sql server2005 step by step(四)
    一步一步学习C#(一)
    SQL实例进阶学习sql server2005 step by step(八)
    SQL Server中常用全局变量和函数 学习sql server2005 step by step(五)
    SQL实例进阶学习sql server2005 step by step(七)
    C#操作excel(开篇)
    SQL进阶提升(平时小积累)学习sql server2005 step by step(十)
    mysql 备份各种方法
    ubuntu 这可怕的弹出窗口啊“Enter password to unlock your login keyring”
    很简单的内核模块A+B
  • 原文地址:https://www.cnblogs.com/jz-597/p/11145284.html
Copyright © 2011-2022 走看看