最小费用最大流——ZKW

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/A1847225889/article/details/81105160

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

KM算法

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

ZKW费用流

到正题了。
类似于KM算法,ZKW算法也有个标号。
D x = D y + 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\n",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());   
}

猜你喜欢

转载自blog.csdn.net/A1847225889/article/details/81105160