首页 > 其他分享 >网络流学习笔记

网络流学习笔记

时间:2023-07-30 11:11:55浏览次数:42  
标签:结点 增广 int 网络 笔记 学习 ans now dis

由于本人太弱,可能讲解有误,请读者指出。

什么是网络流

网络流是通过构建从源点到汇点的有向图模型来解决图论问题。从理论上讲,网络流可以处理所有二分图问题。
二分图和网络流的难度都在于问题建模,一般不会特意去卡算法效率,所以只需要背一两个简单算法的模板就能应付大部分题目了。

最大流问题

什么是最大流

例题

P3376 【模板】网络最大流

解释例题

将一些物品从结点 \(s\) 运输到结点 \(t\),其中可以通过其他的结点中转,每条边都有一条运输物品上限,求最多有多少物品可以从结点 \(s\) 运输到结点 \(t\)。

概念

源点:即结点 \(s\),出发点。
汇点:即结点 \(t\),结束点。
容量:记 \(c(u,v)\),从结点 \(u\) 到结点 \(v\) 的边的运输物品数量上限,若结点 \(u\) 与结点 \(v\) 之间不存在边,\(c(u,v)=0\)。
流量:记 \(f(u,v)\),从结点 \(u\) 到结点 \(v\) 的边实际运输物品数量。
残量:每条边的容量与流量之差。
由于若将物品从结点 \(u\) 运输到结点 \(v\),再又将物品运回来是没有任何意义的,所以可以规定 \(f(u,v)=-f(v,u)\)。
如图:每个边上第一个数是流量,第二个数是容量

性质

通过这些概念,我们就可以从中挖掘出一些性质:

  • 容量限制:\(f(u,v)\le c(u,v)\)。
  • 斜对称性:\(f(u,v)=-f(v,u)\)。
  • 流量平衡:\(\sum\limits_{u\ne\{s,t\},(u,v)\in E}f(u,v)=0\)。

这是最大流的重要性质,同时也是它的条件。

增广路算法

增广路算法思想很简单:从零流开始不断的增加流量,每一次增加要保持三条性质就行了。
我们通过每一条边的残量建边,对对原有边建立反向边,得到一个残量网络,注意这里边的数量可能达到原图的两倍:

这样,我们只需要基于这样的事实:残量网络中的任何一条 \(s\) 到 \(t\) 的有向道路都对应着一条原图中的增广路。只需要求出该道路中的所有残量最小值 \(d\),把所对应的所有边上的流量增加 \(d\) 即可,这个过程就叫增广。如图,红色的就是一条增广路。

不难验证在增广之后它还是满足三条性质的。
如果当图中不存在增广路时,就说明当前流就是最大流了。
这就是增广路定理。

实现——Edmonds-Karp 算法

对于查找路径,我们可以选用 DFS 或 BFS,但是如果用 DFS 则一不小心就会超时,所以应该选用 BFS来实现,时间复杂度 \(O(nm^2)\)。

Code

int Bfs() {
    memset(pre, -1, sizeof(pre));
    for(int i = 1 ; i <= n ; ++ i) flow[i] = INF;
    queue <int> q;
    pre[S] = 0, q.push(S);
    while(!q.empty()) {
        int op = q.front(); q.pop();
        for(int i = 1 ; i <= n ; ++ i) {
            if(i==S||pre[i]!=-1||c[op][i]==0) continue;
            pre[i] = op; //找到未遍历过的点
            flow[i] = min(flow[op], c[op][i]); // 更新路径上的最小值 
            q.push(i);
        }
    }
    if(flow[T]==INF)return -1;
    return flow[T];
}
int Solve() {
    int ans = 0;
    while(true) {
        int k = Bfs();
        if(k==-1) break;
        ans += k;
        int nw = T;
        while(nw!=S) {//更新残余网络 
            c[pre[nw]][nw] -= k;
            c[nw][pre[nw]] += k;
            nw = pre[nw];
        }
    }
    return ans;
}

Dinic 算法

Edmonds-Karp 算法的劣势就在于,每次遍历了一遍残量网络之后只能求出一条增广路来增广。
于是我们针对这一点进行优化,我们就得到了一个更加优秀的替代品,Dinic 算法。
Dinic 算法的关键就是分层图阻塞流

分层图

分层图就是按照结点 \(s\) 到另一个点的最短距离进行分层,相同距离分到一层,就像 \(s\to 第一层\to 第二层\to 第三层\cdots\)。这样就可以得到每一条这样的路径都是 \(s-t\) 最短路。

阻塞流

阻塞流就是指不考虑反向边的“极大流”。每一次增广路后将路径上最小的流量增广,但是减小,再将那条边阻塞。


这就是三次增广后的结果,当图中不存在 \(s-t\) 路径,是增广结束,这就是阻塞流。

时间复杂度

由于最多进行 \(n-1\) 次阻塞流,每次计算不超过 \(O(nm)\),看上去是总时间复杂度是 \(O(n^2m)\),但如果是对于二分图最大匹配这样的图,时间复杂度只有 \(O(n^{dfrac{1}{2}}m)\)。

Code

struct edge
{
    int to, value, net;
} e[N << 1]; //共有n*2条边
void add(int from, int to, int value)
{ //链式前向星
    cnt++;
    e[cnt].to = to;
    e[cnt].value = value;
    e[cnt].net = head[from];
    head[from] = cnt;
}
int bfs(int st, int ed){ //建立层次图
    queue<int> que;
    memset(dis, -1, sizeof(dis));
    dis[st] = 0;
    que.push(st);
    while (!que.empty()){
        int x = que.front();
        que.pop();
        for (int i = head[x]; i > -1; i = e[i].net){
            int now = e[i].to;
            if (dis[now] == -1 && e[i].value){
                que.push(now);
                dis[now] = dis[x] + 1;
            }
        }
    }
    return dis[ed] != -1;
}
int dfs(int x, int t, int maxflow){
    if (x == t)
        return maxflow;
    int ans = 0;
    for (int i = cur[x]; i > -1; i = e[i].net){ ///当前弧优化
        int now = e[i].to;
        if (dis[now] != dis[x] + 1 || e[i].value == 0 || ans >= maxflow)
            continue;
        cur[x] = i;
        int f = dfs(now, t, min(e[i].value, maxflow - ans));
        e[i].value -= f;
        e[i ^ 1].value += f; //反向边加流量
        ans += f;
    }
    if(!ans)dis[x] = -1;
    return ans;
}
int Dinic(int st, int ed){
    int ans = 0;
    while (bfs(st, ed)){
        memcpy(cur, head, sizeof(head));
        int k;
        while ((k = dfs(st, ed, INF)))
            ans += k;
    }
    return ans;
}

标签:结点,增广,int,网络,笔记,学习,ans,now,dis
From: https://www.cnblogs.com/OIerBoy/p/17588820.html

相关文章

  • openGauss学习笔记-25 openGauss 聚集函数
    openGauss学习笔记-25openGauss聚集函数25.1sum(expression)描述:所有输入行的expression总和。返回类型:通常情况下输入数据类型和输出数据类型是相同的,但以下情况会发生类型转换:对于SMALLINT或INT输入,输出类型为BIGINT。对于BIGINT输入,输出类型为NUMBER。对于浮点数输......
  • python数据分析师入门-学习笔记
    第一节数据分析整体介绍应用领域数据分析爬虫开发数据存储数据可视化数据分析内容1.语言基础python基础2.数据获取爬虫课程3.数据存储MySQL数据库4.数据处理NumpyPandas5.数据可视化Matplot......
  • 国产MCU-CW32F030开发学习-BH1750模块
    国产MCU-CW32F030开发学习-BH1750模块硬件平台CW32_48F大学计划板CW32_IOT_EVA物联网开发评估套件BH1750数字型光照强度传感器BH1750BH1750是一款数字型光照强度传感器,能够获取周围环境的光照强度。其测量范围在0~65535lx。lx勒克斯,是光照强度的单位。BH1750可用于调节......
  • 国产MCU-CW32F030开发学习--移植rtthread-nano
    国产MCU-CW32F030开发学习--移植rtthread-nano硬件平台CW32_48F大学计划板CW32_IOT_EVA物联网开发评估套件RT-ThreadNanoRT-ThreadNano是一个极简版的硬实时内核,它是由C语言开发,采用面向对象的编程思维,具有良好的代码风格,是一款可裁剪的、抢占式实时多任务的RTOS。......
  • STM32案例学习 GY-39环境监测传感器模块
    STM32案例学习GY-39环境监测传感器模块硬件平台野火STM32F1系列开发板正点STM32F1系列开发板STM32F103ZET6核心板GY-39环境监测传感器模块GY-39环境监测传感器模块GY-39是一款低成本,气压,温湿度,光强度传感器模块。工作电压3-5v,功耗小,安装方便。其工作原理是,MCU......
  • 国产MCU-CW32F030开发学习-圆形GC9A01_LCD模块
    国产MCU-CW32F030开发学习-圆形GC9A01_LCD模块硬件平台CW32_48F大学计划板CW32_IOT_EVA物联网开发评估套件1.28寸圆形彩色TFT显示屏高清IPS模块240X240SPI接口GC9A01产品介绍1.28寸圆形IPS彩屏,支持RGB65K色显示,显示色彩丰富240X240分辨率,显示清晰IPS全视角面板,超......
  • 【Java】《2小时搞定多线程》个人笔记
    简介基于慕课网站上的一个一元钱课程《2小时搞定多线程》的个人笔记。线程的起源我们先来看看网络中关于线程起源的说明,理解线程的来龙去脉对于掌握多线程有一定帮助。此部分内容整理自下面两篇网络博客:#线程是什么#线程的起源线程的起源与计算机的发展息息相关。早期的计算机系......
  • 学习IDA权威指南-绘图功能
    IDA有丰富的绘图功能通过view-graphs,可以得到五种图形1-外部流程图2-外部函数调用图3-切换视图done......
  • HTML5/CSS3学习——Canvas使用
    什么是Canvas?HTML5的Canvas 元素使用JavaScript在网页上绘制图像。画布是一个矩形区域,你可以控制其每一像素。canvas拥有多种绘制路径、矩形、圆形、字符以及添加图像的方法。 创建Canvas元素向HTML5页面添加Canvas 元素。规定元素的id、宽度和高度:<canvas......
  • 9. 现代循环神经网络
    例如,循环神经网络在实践中一个常见问题是数值不稳定性。尽管我们已经应用了梯度裁剪等技巧来缓解这个问题,但是仍需要通过设计更复杂的序列模型来进一步处理它。具体来说,我们将引入两个广泛使用的网络,即门控循环单元(gatedrecurrentunits,GRU)和 长短期记忆网络(longshort-term......