[MOBAN]多路增广费用流(ZKW费用流)

以前一直很惊异ZKW是什么,,,结果认真学了之后发现原来就是在原本的SPFA费用流上加了点优化?orz orz orz. 我们都知道SPFA费用流是怎么写的.利用EK+SPFA求出从S到T的一条可行的增广最短路径,然后增广这条路径.但这样在某些情况下,实际上是对这张我们好不容易跑出的最短路是有点浪费.(当然这种情况只要出题人不毒瘤其实出现情况真的可能不会很多).因为有可能有多条从源点到汇点的最短路径.在这种情况下可以一次增广多个容量. 所以解决方法----在跑完SPFA之后,我们不只增广一条路径,而尝试利用DFS去尝试最大流的方法去增广.有点类似dinic,但是其增广依据不是依靠dinic我们BFS出的断层dis,而是判断是否dis[起] + len == dis[终],即判断是否是在起点到终点的最短路径上,这些路径都可以增广. 由于写起来很像dinic,因此dinic的当前弧优化也可以用. 详见代码,很好懂. code:
int la[maxm],nt[maxm],owo=1,en[maxm],len[maxm],v[maxm],cur[maxm];
void adg(int x,int y,int z,int vv) {
    en[++owo]=y; nt[owo]=la[x]; la[x]=owo; len[owo]=z; v[owo]=vv;
    en[++owo]=x; nt[owo]=la[y]; la[y]=owo; len[owo]=-z; v[owo]=0;
}
int S,T;
int dis[maxm];
std::queue<int>q;
int mincost; bool rd[maxm];
bool spfa() {
    for(int i=1;i<=tot;i++) dis[i] = 0x3f3f3f3f,rd[i]=0;
    q.push(S); dis[S] = 0; rd[S] = 1;
    while(q.size()) {
        int x = q.front(); q.pop(); rd[x] = 0;
        for(int it=la[x];it;it=nt[it]) {
            int y = en[it];
            if(v[it]&&dis[y]>dis[x]+len[it]) {
                dis[y] = dis[x] + len[it];
                if(!rd[y]) rd[y] = 1, q.push(y);
            }
        }
    }
    if(dis[T]==0x3f3f3f3f) return 0;
    return 1;
}
int dfs(int x,int flow) {
    if(x==T) {
        rd[x] = 1; return flow;
    }
    int dlt = 0;
    rd[x] = 1;
    for(int it=cur[x];it;it=nt[it],cur[x]=it) {
        int y = en[it];
        if( (!rd[y] || y==T ) && v[it] && dis[x] + len[it] == dis[y] ) {
            int tmp = dfs(y,std::min(v[it],flow-dlt));
            v[it] -=tmp,v[it^1]+=tmp;
            mincost+=len[it]*tmp,dlt+=tmp;
            if(dlt==flow) break;
        }
    }
    if(!dlt) dis[x] = -1;
    return dlt;
}
int main() {
    int maxflow = 0;
    while(spfa()) {
        for(int i=1;i<=tot;i++) cur[i] = la[i],rd[i]=0;
        rd[T] = 1;
        while(rd[T]) {
            for(int i=1;i<=tot;i++) rd[i]=0;
            maxflow += dfs(S,0x3f3f3f3f);
        }
}
posted @ 2019-05-24 13:34  Newuser233  阅读(21)  评论(0)    收藏  举报