首页 > 其他 > 详细

上下界网络流——概念解析与快速入门

时间:2019-01-30 00:48:26      阅读:189      评论:0      收藏:0      [点我收藏+]

写这个博客主要是为了自己复习用。这个东西看起来虽然不好理解,但实际上有着非常美妙的性质。(所以比较容易忘QAQ)所以我就打算把它稍微做一点个人笔记啦~

如果对你有帮助的话,记得点个赞哦~

\(Part\) \(1\) :无源汇上下界可行流

概念理解:

  • 没有\(S\)\(T\)节点来统一发出和回收流量。

    • 如果我们把网络流类比成蹄形磁铁的磁感线的话,无源汇上下界可行流就相当于考虑磁铁内部从\(S\)极到\(N\)极磁感线时的情况。
    • 如果不太好理解,您还可以理解为一个循环的管道里面流淌着奇怪的液体(比如液态钾钠合金),然后一直流啊流啊流啊流着在里面循环。
  • 依然要保持流量出入平衡。(每个点进多少出多少)

  • 而且。。。你要保证他的流量在一个\([l, r]\)的范围内

  • 这咋整啊?

我们先啥都不管,对每条边先把下界塞上再讲。这个时候,流量出入显然不平衡,但已经满足了基本的下界要求。填完下界,剩下的容量就可以为所欲为啦。接下来只要在这个基础上,再填上一些必要的流量保持平衡就可以了。

add_edge (u, v, upp[i] - low[i]); // u -> v, flow = upp[i] - low[i]

因为\(low[i]\)被我们事先用完了,所以我们的容量就统一减少了\(low[i]\)这一块。

前面我们已经不计后果地把下界堵着了。自己欠下的锅迟早要背的,这里我们就需要一个补偿流来维护流量的出入平衡。对于每一个点,我们计算它的所有流入流量减去流出流量,得到一个\(flow[i]\)

flow[u] -= low[i];
flow[v] += low[i];

\(flow[i]>0\)说明流进来的比较多,需要一个流出的补偿流。

\(flow[i] < 0\)说明流出去的比较多,需要一个流入的补偿流。

仔细一想,搞补偿流还不简单?直接在前后的边上加流量就好啦。但是事情并没有那么简单——首先这么操作很麻烦,其次\(dinic\)算法是针对有源网络流进行的,循环流它是无能为力的。

然后就有了一个非常伟大的想法——里应不行,我外合不久是了?

所以我们新建一个\(S\)点和一个\(T\)点,接下来的操作就变成了这样:

  • \(flow[i]>0\)说明流进来的比较多,需要一个流出的补偿流。为了引出这个流出的补偿流,我们添加一个流量为\(+flow[i]\)的,由当前点流出到终点的流。

  • \(flow[i] < 0\)说明流出去的比较多,需要一个流入的补偿流。为了引出这个流入的补偿流,我们添加一个流量为\(-flow[i]\)的,由源点流入到当前点的流。

int s = 0, t = n + 1;
for (int i = 1; i <= n; ++i) {
    if (flow[i] < 0) { 
        //流出多 -> 需要流入的补偿流 -> 通过增加外源流出来引出
        add_edge (i, t, -flow[i]);
        add_edge (t, i, 0);      
    } else if (flow[i] > 0){
        //流入多 -> 需要流出的补偿流 -> 通过增加外源流入来引出
        max_flow += flow[i];
        add_edge (s, i, +flow[i]);
        add_edge (i, s, 0); 
    }
}

通过添加相反方向的边,对之前的不平衡作出抵消,我们就实现了出入平衡啦~(也可以像上面那样理解,同样很清楚。)

这样把边一连,套上\(dinic\),我们就跑出来了一个可行流(是当前图的最大流,但不一定真的是所有可以造出来的流中的最大!)根据出入平衡,这里一定有\(∑flow[i] (flow[i] > 0) = max\_flow\) 。通过这个等式,我们就可以检验可行流的存在性啦~

if (max_flow != can_flow) {
    puts ("please go home to sleep");
    return 0;
}

好啦,睡前的最后一个问题:既然我们把本来应该在里面穿插着的补偿流引到了外面,我们又怎么求出所有边使用的可行流大小呢?

像前面所说的:我们添加引向\(S\)\(T\)的边,是为了引出补偿流。所以说,虽然补偿流来的时候不好算,但是走的时候是会留下痕迹哒。(想一下反向边!)所以我们只需要在当前边流量下界的基础上,添加一个当前边的反向边流量(就是之前溜走的补偿流流量啦~),就得到最终的可行流啦~

for (int i = 1; i <= m; ++i) {
    printf ("%d\n", e[((i - 1) * 2) ^ 1].f + low[i]);
}

剩下还有三个延伸拓展的先咕着,回头再说啦\(QwQ\)


上下界网络流——概念解析与快速入门

原文:https://www.cnblogs.com/maomao9173/p/10336427.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!