zoukankan      html  css  js  c++  java
  • 网络流初步

    今天终于接触了传说中的网络流,果然原地爆炸。
    虽然只是掌握了 一点点皮毛,但还是写出来记录一下。

    介绍分析:

    先介绍一些比较枯燥但是也很简单的定义(自己理解的,虽然并不严谨):

    • 源点:只出不进的点
    • 汇点:只进不出的点
    • 容量和流量:容量相当于电阻的额定电流,而流量相当于实际电流,显然,实际电流是不能大于额定电流的。
    • 最大流:相当于源点的最大输入电流
    • 我们这里的节点相当于电学里面的节点,即总输入电流显然等于总输出电流,而且节点不能储存电流
    • 最大流问题就是在给定线路关系和额定电流后询问最大的输入电流。

    那么,如何解决这种问题呢?

    如果我们不是准备用固定算法解决问题,而只是用人脑思考,好像也只能试,即先随意找一条路,并且让最大流为这条路线的最小额定流(这个应该是显然的,否则会烧坏),然后再看还能不能走其他的路,如果还有其他路并且还有路没有达到该线路的最小额定(电流),那么就继续增加最大流,显然更优。但是我们如何能够保证自己找到的路线是最优的呢?即我们如果之前走错了,现在想要反悔应该怎么做呢?

    假设我们现在先用bfs随意找出了一条路线,并且让最大流更新为这条路线的最小额定流,然后第二次再去找,发现找到一半后半程的路线和上一条路线重合,而且因为上一条路线已经达到了容量,所以我们无法再增加流量。假如上一条路线还有其他支路到达汇点,而且可行,那么我们显然是想推掉上一条路线和这条路线重合的部分然后两条路线分别到达,但是我们如何推掉呢?我们这里引入一种方法:增加反向边——即如果我们选定了一条路线,为了方便以后更改,我们给这条路线全部加上容量等于这条路线最大流量的反向边。

     

     

    现在来看为什么加上反向边就可以修改了:




    加入我们有如图所示关系,并且第一次找到的路线为:1--2--3--4

    然后我们进行第二次寻找,发现找到1--3后就不能再进行了,可是从直觉上来看显然最大流为1--2--4和1--3--4,显然不能进行是不对的。

    但是假如我们在第一次寻找的时候都加上了反向边:

    如图所示:第二次我们再进行查找的时候就可以走1--3--2--4,加上上一条路线,即为最优。

    那么,为什么这样做就对了呢?我们可以这样理解:反向边就好像是退回了一条边,反向3--2抵消了上一个路线中的2--3,实际路线仍然是1--2--4和1--3--4

    但是,如何保证这种抵消的正确性呢?我们不难发现:上一条路线和这一条路线的连接点就在那一条反向边,通过设置反向边的容量,我们保证了抵消这条边后剩下的部分可以和上一条路线连接而且不会发生错误。

    实际实现:

    以上就是理解部分,通过这种思想设计出了edmonds-Karp算法:

    题目:

    Drainage Ditches

    Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
    Total Submission(s): 1507    Accepted Submission(s): 701


    Problem Description

    Every time it rains on Farmer John's fields, a pond forms over Bessie's favorite clover patch. This means that the clover is covered by water for awhile and takes quite a long time to regrow. Thus, Farmer John has built a set of drainage ditches so that Bessie's clover patch is never covered in water. Instead, the water is drained to a nearby stream. Being an ace engineer, Farmer John has also installed regulators at the beginning of each ditch, so he can control at what rate water flows into that ditch. 
    Farmer John knows not only how many gallons of water each ditch can transport per minute but also the exact layout of the ditches, which feed out of the pond and into each other and stream in a potentially complex network. 
    Given all this information, determine the maximum rate at which water can be transported out of the pond and into the stream. For any given ditch, water flows in only one direction, but there might be a way that water can flow in a circle.

     

     

    Input

    The input includes several cases. For each case, the first line contains two space-separated integers, N (0 <= N <= 200) and M (2 <= M <= 200). N is the number of ditches that Farmer John has dug. M is the number of intersections points for those ditches. Intersection 1 is the pond. Intersection point M is the stream. Each of the following N lines contains three integers, Si, Ei, and Ci. Si and Ei (1 <= Si, Ei <= M) designate the intersections between which this ditch flows. Water will flow through this ditch from Si to Ei. Ci (0 <= Ci <= 10,000,000) is the maximum rate at which water will flow through the ditch.

     

     

    Output

    For each case, output a single integer, the maximum rate at which water may emptied from the pond.

    Sample Input

    5 4

    1 2 40

    1 4 20

    2 4 20

    2 3 30

    3 4 10

    Sample Output

    50

    #include<cstring>
    #include<cstdio>
    #include<climits>
    #include<cmath>
    #include<queue> 
    using namespace std;
    
    const int MAXN=205;
    int n,m;
    int a[MAXN][MAXN];
    int pre[MAXN];
    int vis[MAXN];
    int u,v,w;
    
    void update_network(int u,int flow)
    {
        while(pre[u]!=-1)
        {
            a[pre[u]][u]-=flow;
            a[u][pre[u]]+=flow;
            u=pre[u];
        }
    }
    
    int find_new(int s,int t)
    {
        memset(vis,0,sizeof(vis));
        memset(pre,-1,sizeof(pre));
        vis[s]=1;
        int min=INT_MAX;
        queue<int> q;
        q.push(s);
        while(!q.empty())
        {
            int cur=q.front(); q.pop();
            if(cur==t) break;
            for(int i=1;i<=m;i++)
            {
                if(vis[i]==0 && a[cur][i]!=0)
                {
                    q.push(i);
                    min=(min<a[cur][i]?min:a[cur][i]);
                    pre[i]=cur;
                    vis[i]=1;
                }
            }
        }
        if(pre[t]==-1)  return 0;
            return min;
    }
    
    int edmonds_karp(int s,int t)
    {
        int new_flow=0;
        int max_flow=0;
        do
        {
            new_flow=find_new(s,t);
            update_network(t,new_flow);
            max_flow+=new_flow;
        }while(new_flow!=0);
        return max_flow; 
    }
    
    int main()
    {
        while(~scanf("%d%d",&n,&m))
        {
            memset(a,0,sizeof(a));
            for(int i=0;i<n;i++)
            {
                scanf("%d%d%d",&u,&v,&w);
                a[u][v]+=w;
            }
            printf("%d
    ",edmonds_karp(1,m));
        }
        return 0;
    }

     

  • 相关阅读:
    几道关于this的经典练习题的理解与分析
    对this的理解与总结
    内存机制及内存泄漏相关总结
    css3-伪元素与伪类
    css3-目标伪类选择器:target的应用
    react学习资料
    angular2学习视频
    vue学习资料
    gulp 入门---使用gulp压缩图片
    gulp 入门---使用gulp压缩css
  • 原文地址:https://www.cnblogs.com/qgmzbry/p/10662064.html
Copyright © 2011-2022 走看看