网络流

#include<iostream>
#include<cstdio>
#include<cmath>
#include<queue>
#include<cstring>
#include<algorithm>
#define lson x<<1
#define rson x<<1|1
#define ll long long
#define rint register int
#define mp(x,y) make_pair(x,y)
using namespace std;
template<typename xxx>void read(xxx &x)
{
    x=0;int f=1;char c=getchar();
    for(;c<'0'||c>'9';c=getchar()) if(c=='-') f=-1;
    for(;c>='0'&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
    x*=f;
}
template<typename xxx>void print(xxx x)
{
    if(x<0){putchar('-');x=-x;}
    if(x>9) print(x/10);
    putchar(x%10+'0');
}
const int maxn=200002;
const int inf=0x7fffffff;
int n,m,s,t,maxflow;
struct edge{
    int to,last,val;
}e[maxn];
int head[maxn],tot;
inline void add(int from,int to,int val)
{
    tot++;
    e[tot].to=to;
    e[tot].val=val; 
    e[tot].last=head[from];
    head[from]=tot;
} 
queue<int>q;
int vis[maxn];
int rem[maxn];//剩余流量
int pre[maxn];
inline int bfs()//用来寻找s,t的最短路并记录,如果s,t不连通则返回0
{
    while(q.size()) q.pop();
    memset(vis,0,sizeof(vis));//当次增广路不能走重点,但每次增广路互不影响 
    q.push(s);vis[s]=1;
    rem[s]=inf;
    while(q.size())
    {
        int x=q.front();q.pop();
        for(rint i=head[x];i;i=e[i].last)
        {
            if(e[i].val)//忽略流量为零 
            {
                int y=e[i].to;
                if(vis[y]) continue;//保证去了的点不在去,也保证下两句正确性 
                rem[y]=min(rem[x],e[i].val);//到达y时最小剩余流 
                pre[y]=i;//连向y的边的编号 
                q.push(y),vis[y]=1;
                if(y==t) return 1;//连通肯定能找到t 
            }
        }
    }    
    return 0;
}
inline void update()//更新最短路与反向边优化 
{
    int x=t;
    while(x!=s)
    {
        int i=pre[x];
        e[i].val-=rem[t];
        e[i^1].val+=rem[t];
        x=e[i^1].to;
    }
    maxflow+=rem[t];
}
int main()
{
    tot=1;//方便正反边转换 
    read(n);read(m);read(s);read(t);
    for(rint i=1;i<=m;i++)
    {
        int a,b,c;
        read(a);read(b);read(c);
        add(a,b,c);add(b,a,0);
    }
    while(bfs()) 
    update();
    print(maxflow);
}
/*
网络流学习
1,通过流量不超过容量
2,除源点汇点外其余点流入流出量相同
3,每条边正反方向流量大小相同方向相反
4,剩余流量为容量减去当前流量
5,最大流的值等于最小割的容量
最小割:就是给你一个图,然后给你边权,问你如何在割掉的边权尽量小的情况下把指定的两个点or两个集合分开
选出一些管道,切断以后,图不连通,这些管道的集合就叫割。这些边的容量之和叫做这个割的容量。
推论 任意一个流都小于等于任意一个割 
考虑FF算法时,残量网络上没有了增广路。
那么我们假设这时候,从源点经过残量网络能到达的点组成的集合为X,不能到达的点为Y。显然汇点在Y里,并且残量网络上没有从X到Y的边。
可以发现以下事实成立:
1.Y到X的边的流量为0.如果不为0,那么一定存在一条从X到Y的反向边,于是矛盾。
2.X到Y的边流量等于其容量。只有这样它才不会在残量网络中出现。
■根据第一个条件得知:没有流量从X到Y后又回到X。所以当前流量应该等于从X到Y的边的流量之和,而根据第二个条件他又等于X到Y的边容量之和。
■而所有从X到Y的边又构成了一个割,其容量等于这些边的容量之和。
★这意味着我们找到一个割和一个流,使得前者的流量等于后者的容量。而根据前边的结论,最大流的流量不超过这个割的容量,所以这个流一定是最大流。
■同样的,最小割的容量也不会小于这个流的流量,所以这个割也一定是最小割。
■而这也正是FF方法的最后局面,由此我们对出结论:FF是正确的,并且最小割等于最大流
6,最大流等于二分图最大匹配数 
7,残量网络=容量网络-流量网络
8,增广路EK算法(FF思想) 十万以内 
    若一条s到t的路径上所有边都有剩余流量,则该路还可以增加新流量,EK是使用bfs不断找增广路直到没有
    每次bfs一条s到t的路径,计算路径上最小值,加入总流量中
    将增广路上所有边的残量减去v,反向边的残量加上v。
    建反向边,留下一个标记,让后面的流有机会让前面的流走另一条路,给程序有反悔的机会,这样再次从反向边流过就相当于抵消了。。 
     每条边代表着一个里面流动着不可压缩流体的具有流速限制的管道, 那么反向增广就有点像是反向加压来尝试把流体压回原来的点, 总的效果就是让这条边里的流速减缓或反向, 而让流返回原来的点重新决策.
     n*m*m(n为点,m为边),边少时用 
*/     
全部评论

相关推荐

评论
点赞
收藏
分享
牛客网
牛客企业服务