luogu3381【模板】最小费用最大流(zkw费用流板子)

来源:互联网 发布:外交部流氓回答知乎 编辑:程序博客网 时间:2024/06/13 21:21

%%%xtx的代码了好久,又翻了很多文章,终于算是差不多看懂了???
费用流问题,是建立在最大流问题基础之上的,也就是说,现在流要钱了,我们要求出最大流,可能有很多个最大流,我们还要费用最小的那个。比较直观的一个想法便是:把原来的bfs分层变成spfa等最短路算法,求出从s到t费用最小的一条路径来增广。然而这样我们就失去了Dinic的一个优势,即多路增广。因为每次我们进行增广后,最短路都不一定再满足了,我们需要重新运行spfa来算。这样就比较慢。zkw费用流改进在哪里呢?他使用了类似km算法的每次修改顶标的做法。

任何一个最短路算法保证, 算法结束时对任意指向顶点 i 、从顶点 j 出发的边满足 DiDj+cij (条件1), 且对于每个 i 存在一个 j 使得等号成立 (条件2). 换句话说, 任何一个满足以上两个条件的算法都可以叫做最短路, 而不仅仅是 SPFA、Dijkstra, 算法结束后, 恰在最短路上的边满足 Di=Dj+cij.

在最小费用流的计算中, 我们每次沿 Di=Dj+cij 的路径增广后都不会破坏条件 1, 但是可能破坏了条件 2. 不满足条件 2 的后果是什么呢? 使我们找不到每条边都满足 Di=Dj+cij 新的增广路. 只好每次增广后使用 Dijkstra, SPFA 等等算法重新计算新的满足条件 2 的距离标号. 这无疑是一种浪费. KM 算法中我们可以通过不断修改可行顶标, 不断扩大可行子图, 这里也同样, 我们可以在始终满足条件 1 的距离标号上不断修改, 直到可以继续增广 (满足条件 2).

Di+cij≥Dj ⇔ Di−Dj+cij≥0 ①
Di+cij=Dj ⇔ Di−Dj+cij=0 ②
对于一个顶标D,我们可以不断的dfs找Di−Dj+cij=0的增广路经
假设我们当前dfs失败
即使失败还是有一些点能满足Di−Dj+cij=0的
这些点被我们当前dfs到了
我们记这些点的点集为V
找到Δ=min{DiDj+cij}|iV,jV,flowij>0
然后我们对 iV,Dπi=DiΔ

证明:
弧(i,j)可以分成四类,再根据当前dfs失败的条件,有:

i∈V,j∉V 原来Di−Dj+cij≥Δ>0 新图 DπiDj+cij0

i∈V,j∈V 原来Di−Dj+cij=0 新图 DπiDπj+cij=0

i∉V,j∉V 原来Di−Dj+cij≥0 新图 DiDj+cij0

i∉V,j∈V 原来Di−Dj+cij≥0 新图 DiDπj+cij>=Δ

可以发现第一类弧中一定有至少一条满足
原来Di−Dj+cij=Δ 新图DπiDj+cij=0
即至少有一条新的边进入了 Dj=Di+cij 的子图。

这样直到找不到这样的Δ,就不会再有增广路了。
本来我们要记录D数组,每次修改距离标记改的是D数组,但是我们也可以不计这个D数组,每条边(u,v)的c就表示DuDv+cuv,则我们每次减正向边,加反向边,也是一样的效果。那么在最短路径上的边,也就是c为0的边。

#include <bits/stdc++.h>using namespace std;#define ll long long#define N 5010#define inf 0x3f3f3f3finline int read(){    int x=0,f=1;char ch=getchar();    while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();    return x*f;}int n,m,s,t,h[N],num=1,cost=0,price=0,mxflow=0;bool vis[N];struct edge{    int to,next,w,c;}data[50010<<1];inline void add(int x,int y,int w,int c){    data[++num].to=y;data[num].next=h[x];h[x]=num;data[num].w=w;data[num].c=c;    data[++num].to=x;data[num].next=h[y];h[y]=num;data[num].w=0;data[num].c=-c;}inline int dinic(int x,int low){    vis[x]=1;if(x==t){cost+=low*price;mxflow+=low;return low;}int tmp=low;    for(int i=h[x];i;i=data[i].next){        int y=data[i].to;if(vis[y]||!data[i].w||data[i].c) continue;        int res=dinic(y,min(tmp,data[i].w));        tmp-=res;data[i].w-=res;data[i^1].w+=res;        if(!tmp) return low;    }return low-tmp;}inline bool label(){    int d=inf;    for(int x=1;x<=n;++x){        if(!vis[x]) continue;        for(int i=h[x];i;i=data[i].next){            int y=data[i].to;if(vis[y]) continue;            if(data[i].w&&data[i].c<d) d=data[i].c;        }    }if(d==inf) return 0;    for(int x=1;x<=n;++x){        if(!vis[x]) continue;        for(int i=h[x];i;i=data[i].next)            data[i].c-=d,data[i^1].c+=d;    }price+=d;return 1;}int main(){//  freopen("a.in","r",stdin);    n=read();m=read();s=read();t=read();    while(m--){        int x=read(),y=read(),w=read(),c=read();add(x,y,w,c);    }do         do memset(vis,0,sizeof(vis));while(dinic(s,inf));    while(label());    printf("%d %d\n",mxflow,cost);    return 0;}
原创粉丝点击