网络流自学心得[最小割最大流定理+最小费用最大流问题]

网络流中还有两类重要的问题,一个是最小割最大流问题,还有一个是最小费用最大流问题

最小割最大流定理

先说结论,这个定理陈述了这么一个事实,一个网络流的最小割是等于这一个网络流的最大流。
证明过程可以详见定理证明过程

然后我讲讲我的理解。

我记得有一个形象的比较。 网络流就相当于你家里的所有水管,自来水厂是超级源点,而你家的出水口就是汇点。
现在有一个匪徒,想通过砍断你家的水管,使得出水口出不出来水(就相当于断流),问怎么砍最省力。那当然是找最细的水管去砍,就省力是吧。
我们呢,可以把整个图分成两个集合,一个叫做S集合,一个叫做T集合。而S集合就是一类与源点s相连接的点,而T集合就是一类与汇点t相连接的点。当然,S集合和T集合是相互连接的。而最小割是什么意思呢,就是我们找到一些边,使得这些边被剪掉的时候,那么S集合和T集合不相通,然后总有一个情况,这些边的流量是最小的吧,那就是最小割了。

那么为什么最小割和最大流有关系呢?

还记得我们求最大流问题时候用的算法吗?我们先找增广路,就相当于找到一条通往汇点的路。找到之后,我们让增广路上的所有边的流量减去最小的边的流量,然后将这个流量增加到我们最后要输出的那个最大流上。
其实这里不就有了一个最小割的影子吗?那个流量最小的边减去自己的时候,流量就变成了零,就相当于这条边断掉了(当然,反向边不是这条边)。所以其实我们在求最大流的时候,实际上就是找出最小割,然后将他们的流量相加,最后就成了最大流。这么想的话,最大流也就是在我们在一张网络流的图中,找出最小的边,使得图流不同,从而汇聚成最大流。最小和最大的转化,引人深思= =。
所以我们在解决问题的时候,就可以从这两个方面入手。当一边的问题想不通的时候,我们就从另一边入手,可能就有解决问题的办法。

最小费用最大流问题

这类问题的目标是这样子的:在追求最大流的前提下,我们找一些最短的路(即这些路的费用和最小),使他完成最大流的任务。
这就像我们开着车要把一些货送到重点。当然送的货越多越好。但是通过很多道路的时候呢是要收费的,那我们怎样在将货运的最多的前提下,保证我们路费最少。

这类问题其实已经不是一个简单的网络流的问题了。因为我们要找最小费用,把费用变成路长,那就是要我们找最短路了是吧。所以我们处理这类问题可以这么解决。我们每次从起点出发,找到一条最短路,然后算这条路上的网络流(松弛balabala的)。然后继续找最短路,直到我们找不到最短路了,那就说明这个网络流的图结束了。每一条最短路,其实都是一条增广路。

这里我们用spfa算法,求最短路,用最短路算法判断是否还存在增广路,在对这一条路进行流处理。这突然感觉有点像dinic算法。。

edge e[len*len];
char M[len][len];
int pre[len], inq[len];//pre  前驱数组  inq 是否在队列之中
int head[len], dis[len];//dis  路径数组
int n, m, hcnt, s, t;
bool spfa(int s, int t){
    queue <int> q;
    q.push(s);
    memset(inq, 0, sizeof (inq));
    memset(pre, -1, sizeof (pre));
    for(int i=s; i<=t; i++) dis[i] = INF;
    dis[s] = 0;

    while(!q.empty()){
        int h = q.front(); q.pop();

        for (int i=head[h]; ~i; i=e[i].next){
            int v = e[i].v;
            if (e[i].f && dis[v] > dis[h] + e[i].c){
                dis[v] = dis[h] + e[i].c;
                pre[v] = i;
                if (!inq[v]){
                    inq[v] ++;
                    q.push(v);
                }
            }
        }
        inq[h] = 0;
    }
    if (dis[t] == INF) return false;
    else return true;

}
void dfs(int s, int t){
    int cost = 0;
    while(spfa(s, t)){
        int minf = INF;
        for(int i=pre[t]; ~i; i=pre[e[i].u])
            if (e[i].f < minf) minf = e[i].f;

        for (int i=pre[t]; ~i; i=pre[e[i].u]){
            e[i].f -= minf;
            e[i^1].f += minf;
        }

        cost += minf * dis[t];
    }

    cout << cost << endl;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值