G : 最大流问题

简介: 这篇文章介绍了最大流问题的基本概念和求解方法,并通过C++代码实现了使用广度优先搜索(BFS)和残余网络来计算有向图中从源点到汇点的最大流。

G : 最大流问题

Description

给出一张网络有向图,源点及汇点,计算其最大流。 该网络中的顶点编号为1~N,发点和汇点在输入中指定。

Input

第一行给出四个整数N(1 <= N <= 200), M(N <= M <= 5000),S,T,分别表示节点数量、有向边数量、源点序号、汇点序号。

接下来M行每行包括三个正整数ui,vi,wi,表示第 i条有向边从 ui出发,到达 vi,边权为 wi。0<wi<=10000

Output

一行,包括一个正整数,为该网络流最大流。

Sample Input

6 9 4 2
1 3 10
2 1 20
2 3 20
4 3 10
4 5 30
5 2 20
4 6 20
5 6 10
6 2 30

Sample Output

50

Hint

问题表述:给定一幅图(n个结点,m条边),每一条边有一个容量,现在需要将一些物品从结点s(称为源点)运送到结点t(称为汇点),可以从其他结点中转,求最大的运送量。

在介绍最大流问题的解决方法之前,先介绍几个概念.

网络:网络是一个有向带权图,包含一个源点和一个汇点,没有反向平行边。

网络流:网络流即网上的流,是定义在网络边集E上的一个非负函数flow={flow(u,v)}, flow(u,v)是边上的流量。

可行流:满足以下两个性质的网络流flow称为可行流。

容量约束:每条边的实际流量不能超过改变的最大流量。

流量守恒:除了源点s和汇点t之外,所有内部节点流入量等于流出量。

源点s:源点主要是流出,但也有可能流入。

源点的净输出值=流出量之和-流入量之和。

汇点t:汇点主要是流入,但也有可能流出。

汇点的净输入值=流入量之和-流出量之和。

对于一个网络可行流flow,净输出等于净输入,这仍然是流量守恒。

网络最大流:在满足容量约束和流量守恒的前提下,在流网络中找到一个净输出最大的网络流。

反向弧:若从u到v的边的容量为c ,这条边上有流量 f 流过(称为正向弧),则相当于v到u有一条容量为0的边,其流量为- f ,这条边就是反向弧。反向弧的作用主要是用于寻找增广路。

反向弧的意义:反向弧的作用是起到有更优决策的时候会使当前选择的弧会自动放弃。反向弧有流量的原因是因为如果刚刚选择了正向弧,下一次如果存在更优策略使这f的流量流入汇点,就可以选择反向弧,将流量 f 撤销。

残余网络:计算出图中的每条边上容量与流量之差(称为残余容量),即可得到残余网络。注意由于反向边的存在,残余网络中的边数可能到达原图中边数的两倍。

增广路径:残余网络中任何一条从s到t的有向道路都对应一条原图中的增广路径 —— 只要求出该道路中所有残量的最小值d ,把对应的所有边上的流量增加d 即可,这个过程称为增广。

最大流定理:如果残留网络上找不到增广路径,则当前流为最大流;反之,如果当前流不为最大流,则一定有增广路径。

二、AC代码

#include <iostream>
#include <cstring>
#include<queue>

#include<vector>
using namespace std;
int n,m;
const int maxn=1005;
int g[maxn][maxn];//容量
int f[maxn][maxn];//流量
int vis[maxn];
int pre[maxn];//前驱数组
const int INF =1111111;

int bfs(int s,int t)
{
    memset(pre,-1,sizeof(pre));//初始化数组
    memset(vis,0,sizeof(vis));

    queue<int > q;
    vis[s]=1;//记录时间
    q.push(s);//加入队列
   // cout<<s<<t<<endl;

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

        for(int i=1;i<=n;i++)
        {
            if(!vis[i]&&g[now][i])
            {

                vis[i]=1;
                pre[i]=now;
                if(i==t)return 1;//到终点了就退出
                q.push(i);

            }
        }
    }
    return 0;
}

 int u,v;
int EK(int s,int t)
{
    int maxflow=0;

    while(bfs(s,t))//bfs找路径
    {
        int d=INF;
        v=t;
        while(v!=s)//利用前驱回置到最初s,并求出最小流,满足容量约束
        {
            //获取前驱
            u=pre[v];

            //求路径上的最小容量
            if(d>g[u][v])d=g[u][v];

            //向前迭代
            v=u;
        }

        maxflow+=d;//最大流=+所有路径上的最小容量
       // cout<<d<<endl;
        v=t;
        while(v!=s)
        {
            //获取前驱
            u=pre[v];

            //求增广路径
            g[u][v]-=d;
            g[v][u]+=d;//满足流量约束,输入输出流相等
            if(f[v][u]>0)f[v][u]=-d;
            else f[u][v]+=d;

             //向前迭代
            v=u;
        }

    }
    //cout<<maxflow<<endl;;
    return maxflow;
}
int main()
{
    int s,t;
    while(cin>>n>>m>>s>>t)
    {
        memset(f,0,sizeof(f));
        memset(g,0,sizeof(g));
        for(int i=0;i<m;i++)
        {
            int u,v,w;
            cin>>u>>v>>w;
            g[u][v]+=w;
        }
        cout<<EK(s,t)<<endl;

    }
    return 0;
}
相关文章
土方量的几种计算方法
土方量的几种计算方法
292 1
202012-2 期末预测之最佳阈值
202012-2 期末预测之最佳阈值
|
算法
平稳限流?突发限流?还是时间窗口?三种限流算法分析与对比
漏桶限流算法和令牌桶限流算法是两种常见的限流算法,它们的原理和实现方式有所不同。 漏桶限流算法 漏桶限流算法是一种固定容量的桶,水以恒定的速率流出,来限制请求的流量。当请求到来时,会先加入到漏桶中,漏桶以恒定的速率处理请求,处理不了的请求会被丢弃。 以下是漏桶限流算法的流程图:
134 0
|
算法 数据建模
最小费用最大流问题详解
最小费用最大流问题详解
997 0
最小费用最大流问题详解
从概率统计看限流阈值问题
服务提供方所在的应用A有100台机器,某个调用方需要申请该应用的某个服务1000 QPS的调用量,根据经验,如果A应用的单机限流值配置成10,应该会有比较多的额度内的流量被限制住,如果配置的太高,又会有稳定性风险,限流值应该配置成多少合适呢?
262 1
LeetCode 5958. 股票平滑下跌阶段的数目(滑动窗口)
LeetCode 5958. 股票平滑下跌阶段的数目(滑动窗口)
194 0
|
算法
算法学习之路|网络流之最大流
最大流可以看做是把一些东西从源点s送到汇点t,可以从其他的点中转,每条边最多只能输送一定的物品,求最多可以把多少东西从s送到t,这样的问题就是最大流问题。
1152 0