首页 > 其他分享 >网络流初步

网络流初步

时间:2025-01-01 19:41:03浏览次数:1  
标签:增广 int 网络 流量 初步 dep frz

网络流初步(脑部整理)

呜呜呜,家人们也是学上网络流了。

咸鱼起手,你反应得过来吗?

英语不太好(老英不会看窝博客吧)

What is 网络流?

概述

网络\((network)\)是指一个特殊的有向图 \(G=(V,E)\),其与一般有向图的不同之处在于有容量和源汇点。

  • $E $中的每条边 $ (u, v)$ 都有一个被称为容量 \((capacity)\)的权值,记作 $ c(u, v)$。当 \((u,v)\)
    $\notin E $时,可以假定 \(c(u,v)=0\)。
  • $V $ 中有两个特殊的点:源点 \((source)s\) 和汇点 \((sink)t(s \neq t)\)。

对于网络 $ G=(V, E)$,流 \((flow)\) 是一个从边集 E 到整数集或实数集的函数,其满足以下性质。

  • 容量限制:对于每条边,流经边的流量不得超过该边的容量,即 $ 0 \leq f(u,v) \leq c(u,v)$ ;
  • 流守恒性:除源汇点外,任意结点$ u$ 的净流量为$ 0$。

其中,我们定义 $u $的净量为 \(f(u) = \sum_{x \in V} f(u, x) - \sum_{x \in V} f(x, u)\)。对于网络$ G = (V, E) $和其上的流 \(f\),我们定义 $ f$ 的流量 $ |f| $为
$s $的净流量 $ f(s)$。作为流守恒性的推论,这也等于 $ t$ 的净流量的相反数 \(-f(t)\)。对于网络 \(G = (V, E)\),如果 ${S, T} $是 \(V\) 的划分(即 \(S \cup T = V 且 S \cap T = \varnothing\)),且满足$ s \in S, t \in T$,则我们称 \(\{S, T\}\) 是 \(G\) 的一个 \(s-t\) 割(cut)。我们定义 \(s-t\) 割$ {S, T}$ 的容量为 \(||S, T|| = \sum_{u \in S} \sum_{v \in T} c(u, v)\)。

--以上摘自\(OI-Wiki\)。

靠! 什么玩意?都是中文咋看不懂?

于是我们找到了启蒙例(ti)题(jie)。然后偷税得发现,其实就是一堆的水管,每个水管有相应的容量\((c)\),并且水管里的水只能单向流动,接着让我处理一些有关流量\((f)\)的头大有趣问题。

可以发现,对于一张网络,有这么几个重点。

  • 容量限制:$f(u,v) \leq c(u,v) $。一条边上的流量不能大于水管容量(不然就要爆炸)。
  • 相反净流量:\(f(u,v)=-f(v,u)。\)由\(u\)到\(v\)的净流量必须是\(v\)到\(u\)净流量的相反数。
  • 流守恒性:除非\(u=s\)或\(u=t\),否则\(\sum_{x \in V} f(u, x)=0\)。一个非源非汇的点的净流量一定是\(0\)。

最大流

根据名字显然可以知道最大流就是最大流量的意思

其实就是以下意思:

【模板】网络最大流

题目描述

如题,给出一个网络图,以及其源点和汇点,求出其网络最大流。

输入格式

第一行包含四个正整数 \(n,m,s,t\),分别表示点的个数、有向边的个数、源点序号、汇点序号。

接下来 \(m\) 行每行包含三个正整数 \(u_i,v_i,w_i\),表示第 \(i\) 条有向边从 \(u_i\) 出发,到达 \(v_i\),边权为 \(w_i\)(即该边最大流量为 \(w_i\))。

输出格式

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

样例 #1

样例输入 #1

4 5 4 3
4 2 30
4 3 20
2 3 20
2 1 30
1 3 30

样例输出 #1

50

提示

样例输入输出 1 解释

题目中存在 \(3\) 条路径:

  • \(4\to 2\to 3\),该路线可通过 \(20\) 的流量。
  • \(4\to 3\),可通过 \(20\) 的流量。
  • \(4\to 2\to 1\to 3\),可通过 \(10\) 的流量(边 \(4\to 2\) 之前已经耗费了 \(20\) 的流量)。

故流量总计 \(20+20+10=50\)。输出 \(50\)。


数据规模与约定

  • 对于 \(30\%\) 的数据,保证 \(n\leq10\),\(m\leq25\)。
  • 对于 \(100\%\) 的数据,保证 \(1 \leq n\leq200\),\(1 \leq m\leq 5000\),\(0 \leq w\lt 2^{31}\)。

青铜 — — Ford-Fulkerson算法(增广路算法)

首先我们先找到一条从\(s\)到\(t\)容量不为零的路径,就是传说中的增广路,然后把一条条增广路加在一起,似乎就可以得到一个答案。

但如果选择的路径较劣就会影响最后的答案。

那怎么办呢?增广路真的一点鸟用都没有吗?

当然,部分人类凭借高级的大脑想出了处理方法——建一条反方向的边。正向边减少的同时给反向边进行增加,即利用\(f(u,v)=-f(u,v)\)这样就可以实现类似于反悔贪心的操作,至于正确性。先引入一个叫做残量网络的概念。可以理解为把满流的边扔掉后,边权为剩余流量的图。(包括反向边)。当在残量网络中流过一条反向边时,相当于将原先的流压了回去,而通过列举简单的例子,可以发现流经反向边事实上是一个反悔的等价操作。

每次只需要在残量网络中找增广路即可。

局限性

但这样跑肥肠靠RP,只需要简单造造数据就可以把算法卡成 \(O(值域)\) 的,显然是指数级算法。

那这算法有个蛋用啊?表演T飞吗?但这个算法可以打暴力通过合理优化达到高效的运行。但增广路算法是基础。

Edmonds-Karp算法(EK算法)

显然地,我们可以每次增广最短路,把最短路压榨干净后,这条路就在残量网络中OUT了。而这条跑满流的边也就是关键边

每一个增广路意味着至少有一条关键边出现,而增广过程的迭代次数也就可以看做是每条边成为关键边的次数。

这样总迭代次数为 \(O(V\cdot E)\) ,加上0/1最短路的 \(O(E)\),总复杂度也就是 \(O(V \cdot E^{2})\)。

局限性

因为 \(O(E)\) 与 \(O(V^{2})\)同阶,那复杂度也可以看做是 \(O(V^{5})\)。

这时候就有人说了:虽然复杂度已经降到了一个多项式的程度,但还是难以让人接受,有没有更快更好的算法呢?

有的,有的,包有的兄弟。

咱先想想EK为啥不够快:
我们可以很显然地艰难地发现,EK每次只会跑一条最短路,但我们最短路可能不止一条啊。

基于这一点,我们继续进行优化。

Dinic算法

没有什么是一个BFS或一个DFS解决不了的;如果有,那就两个一起。

Dinic的核心思想就是在EK的基础上进行多路增广。

这需要搞出分层图,而这里分层图的定义可以理解为:对于每个点,到达源点s的最短路大小。也可以认为是一个点的深度。

分完层之后有什么好处呢?首先层内边和反向边都会被无视掉。
其次我们可以发现,此时所有的最短路都在图上呈现。这样DFS一遍就能把这张图上的最短路全都干掉。

似乎这样可以跑得飞快。

但是的但是,我们会重复搜到同一个点,但通过这个点的流量不一定已经到达最大值,所以不能直接标记掉,但无标记的DFS直接爆炸。

so 优化了个寂寞???

No!No!No!

我们再来加两只优化(咋这么多优化)。

代码解释。

struct node{int to,c,lp;};
vector<node>g[N];
int dep[N],pos[N];
int n,m,S,T;
inline void add(int from,int to,int c)//存边包括正向边和反向边
{
    g[from].push_back((node){to,c,(int)g[to].size()});//正向
    g[to].push_back((node){from,0,(int)g[from].size()-1});//反向,g[from]已经加入新值,下标size-1
}
inline void fc(int s)//深度信息
{
    for(int i=0;i<=n;i++)dep[i]=-1;
    queue<int>q;//储存节点编号
    dep[s]=0;
    q.push(s);
    while(!q.empty())
    {
        int u=q.front();q.pop();
        for(auto v:g[u])
        {
            if(v.c>0&&dep[v.to]<0)//容量大于0且没被计算过
            {
                dep[v.to]=dep[u]+1;
                q.push(v.to);
            }
        }
    }
}
inline int dfs(int u,int t,int f)
{
    if(u==t||!f) return f;
    int ans=0;
    for(int &i=pos[u];i<g[u].size();i++)//当前弧优化,重复经过同一个节点时,跳过已经跑满的边,巧妙地更新pos值实现
    {
        auto &x=g[u][i];//g[u][i]的信息要被修改
        if(x.c>0&&dep[u]+1==dep[x.to])//有容量且层数相邻
        {
            int frz=dfs(x.to,t,min(f,x.c));
            if(frz>0)
            {
                x.c-=frz;g[x.to][x.lp].c+=frz;
                f-=frz;ans+=frz;
                if(!f) break;//剪枝,现在找不到妹子,以后也找不到。
            }
        }
    }
    return ans;
}
inline ll dinic(int s,int t)
{
    ll max_flow=0;
    while(1)
    {
        fc(s);
        if(dep[t]==-1)return max_flow;
        for(int i=0;i<=n;i++)pos[i]=0;
        max_flow+=dfs(s,t,inf); 
    }
}
inline void solve()
{
    n=read();m=read();S=read();T=read();
    for(int i=1;i<=m;i++)
    {
        int x,y,c;
        x=read();y=read();c=read();
        add(x,y,c);
    }
    write(dinic(S,T));
}

完整板子:

#ifdef ONLINE_JUDGE 
#else 
#define Qiu_Cheng 
#endif 
#include <bits/stdc++.h> 
// #define int long long 
using namespace std; 
typedef long long ll; 
const int N=205,mod=1e9+7,inf=INT_MAX; 
// const int mod1=469762049,mod2=998244353,mod3=1004535809;
// const int G=3,Gi=332748118; 
// const int M=mod1*mod2;
inline int read()
{
    int x=0,f=1;
    char c=getchar();
    while(c<'0'||c>'9'){if(c=='-'){f=-1;}c=getchar();}
    while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c-'0');c=getchar();}
    return x*f;
}
inline void write(ll x)
{
    if(x<0){putchar('-');x=-x;}
    if(x>9) write(x/10);
    putchar(x%10+'0');
}
struct node{int to,c,lp;};
vector<node>g[N];
int dep[N],pos[N];
int n,m,S,T;
inline void add(int from,int to,int c)
{
    g[from].push_back((node){to,c,(int)g[to].size()});
    g[to].push_back((node){from,0,(int)g[from].size()-1});
}
inline void fc(int s)//深度信息
{
    for(int i=0;i<=n;i++)dep[i]=-1;
    queue<int>q;//储存节点编号
    dep[s]=0;
    q.push(s);
    while(!q.empty())
    {
        int u=q.front();q.pop();
        for(auto v:g[u])
        {
            if(v.c>0&&dep[v.to]<0)
            {
                dep[v.to]=dep[u]+1;
                q.push(v.to);
            }
        }
    }
}
inline int dfs(int u,int t,int f)
{
    if(u==t||!f) return f;
    int ans=0;
    for(int &i=pos[u];i<g[u].size();i++)//当前弧优化
    {
        auto &x=g[u][i];//g[u][i]的信息要被修改
        if(x.c>0&&dep[u]+1==dep[x.to])//有容量且层数相邻
        {
            int frz=dfs(x.to,t,min(f,x.c));
            if(frz>0)
            {
                x.c-=frz;g[x.to][x.lp].c+=frz;
                f-=frz;ans+=frz;
                if(!f) break;//剪枝
            }
        }
    }
    return ans;
}
inline ll dinic(int s,int t)
{
    ll max_flow=0;
    while(1)
    {
        fc(s);
        if(dep[t]==-1)return max_flow;
        for(int i=0;i<=n;i++)pos[i]=0;
        max_flow+=dfs(s,t,inf); 
    }
}
inline void solve()
{
    n=read();m=read();S=read();T=read();
    for(int i=1;i<=m;i++)
    {
        int x,y,c;
        x=read();y=read();c=read();
        add(x,y,c);
    }
    write(dinic(S,T));
}
signed main() 
{ 
#ifdef Qiu_Cheng 
    freopen("1.in","r",stdin); 
    freopen("1.out","w",stdout); 
#endif 
    // ios::sync_with_stdio(false); 
    //cin.tie(0); cout.tie(0); 
    // int QwQ; 
    // cin>>QwQ; 
    // while(QwQ--)solve(); 
    solve(); 
    return 0; 
}
//12 825076913 0 173167432
//  6666   66666  666666 
// 6    6  6   6      6 
// 6    6  6666      6 
// 6    6  6  6    6 
//  6666   6   6  6666666

//g++ -O2 -std=c++14 -Wall "-Wl,--stack= 536870912 " cao.cpp -o cao.exe

标签:增广,int,网络,流量,初步,dep,frz
From: https://www.cnblogs.com/qc0817/p/18646224

相关文章

  • 计算机网络原理(一)
    嘿!新年的第一篇博客,大家新年快乐呀!希望大家新的一年要多多进步噢!1.TCP/IP的四层/五层参考模型有哪些层,各层的特点是?计算机网络分层的好处是?TCP/IP四层参考模型应用层:直接为用户的应用程序提供服务传输层:为应用层实体提供端到端的通信功能,保证了数据包的顺序传送及数......
  • 第16章 网络
    第16章网络纲要.NETFramework在System.Net.*命名空间中包含了支持各种网络标准的类,支持的标准包括HTTP、TCP/IP以及FTP等。以下列出了其中的主要组件:​Webclient​类支持通过HTTP或者FTP执行简单的下载/上传操作。​WebRequest​和WebResponse​类可以......
  • 内网渗透:网络认证机制
    文章目录一、网络认证概述什么是网络认证?常见的网络认证方式二、NTLM协议挑战响应认证机制基本流程成功认证流程三、NTLM抓包分析实验环境实验步骤抓包分析要点四、Challenge和Response分析Challenge和Response的作用Response的生成过程五、NTLMv1和NTLMv......
  • 网络网络层的逻辑图
    网络层逻辑图架构 网络层与上下层关系 -与上层关系:接收传输层的数据段,将其封装成数据包,为传输层提供逻辑通信服务,根据传输层需求选择合适路由。-与下层关系:将数据包交给数据链路层,由其负责将数据在物理网络上传输,接收来自数据链路层的帧,抽取其中数据包进行处理。 ......
  • 计算机网络•自顶向下方法:路由选路算法
    路由选路算法在网络层中,选路是指数据包从源主机到目的主机的传输过程中,如何通过网络中的路由器选择一条合适的路径。路由器根据网络拓扑、路由表、协议规则等来决定如何将数据包转发到下一跳,直到数据包到达目的地。选路算法分类静态算法or动态算法静态算法:路由随时间......
  • 计算机网络•自顶向下方法:DHCP、NAT、IPV6
    获取IP地址路由器:管理员手工配置路由器各个接口的IP地址主机:管理员手工配置主机IP地址,服务器通常采用这种方法使用动态主机配置协议DHCP(DynamicHostConfigurationProtocol)获取IP地址、子网掩码、缺省路由器、本地DNS服务器等配置信息,个人终端通常采用这种方法使用DH......
  • 《新年,给网络安全来一场 “大扫除”》
    《新年,给网络安全来一场“大扫除”》当新年的钟声敲响,我们怀着对新一年的憧憬,忙着整理生活的方方面面,为开启全新篇章做准备。而在这个数字化时代,网络已然成为我们生活的重要“主场”,与之相伴的网络安全问题,也如同角落里隐匿的灰尘,随着时间的推移悄然累积。此刻,不妨趁着新年......
  • 【深度学习基础|知识概述】神经网络基础中的神经元结构是怎么样的?以及常用的激活函数
    【深度学习基础|知识概述】神经网络基础中的神经元结构是怎么样的?以及常用的激活函数有哪些?各有什么优缺点和应用场景。附公式及代码。(二)【深度学习基础|知识概述】神经网络基础中的神经元结构是怎么样的?以及常用的激活函数有哪些?各有什么优缺点和应用场景。附公式及代码。......
  • Nodejs的网络模块都有几个?
    在Node.js中,网络相关的模块主要包括但不限于以下几个:HTTP模块:这是Node.js中用于处理HTTP请求和响应的核心模块。通过它,开发者可以创建HTTP服务器和客户端,实现基于HTTP协议的网络通信。Net模块:Net模块提供了创建网络服务器和客户端的能力,它支持TCP、IPC等协议,使得计算机或设......
  • Docker网络与数据卷持久化
    由于格式和图片解析问题,为了更好的阅读体验,可以前往阅读原文docker中网络的概念也是非常重要,它对于容器资源的隔离也起着非常重要的作用。你有没有在启动一个容器后查看它的ip,假如你启动了一个nignx容器,你想在主机上访问它,首先得知道他的ip地址,可以通过以下方式获取:dockeri......