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

学习笔记:网络流

时间:2023-01-02 17:24:50浏览次数:45  
标签:last 增广 int flow 网络 笔记 学习 read 流量

学习笔记 网络流

基本概念

一个网络是一张有向无环图,每条边有容量 \(w\),表示这条边最多能容纳多少流量,入度为0的点叫源点 \(s\),出度为0的点叫汇点 \(t\),源点有无限流量可以提供,最终要流向汇点,每个点流量守恒,即流入量等于流出量。

残量网络:找完增广路后并把边的容量相应更新后剩下的网络。

最大流

最大流问题是问从源点能流到汇点的最大流量是多少。

引入反向边:初始容量为0,当u给v流了w的流量,反向边容量增加w,当流量流经反向边时相当于在原边上退流,相当于提供了反悔机会。

增广路:从源点到汇点的一条流量可流过的路径。

最大流算法本质是找增广路。

EK算法

用队列增广,记录每个点的流向它的边last,增广到汇点后把边的容量更新,重复此过程直到找不到增广路。

复杂度\(O(nm^2)\)

点击查看代码
#include <bits/stdc++.h>
#define int long long
#define endl putchar('\n')
using namespace std;
const int M = 2e3+10;
const int inf = 2147483647;
typedef long long ll;
inline int read(){
    int x=0,f=0;char c=getchar();
    while(!isdigit(c)){
        if(c=='-') f=1;c=getchar();
    }
    do{
        x=(x<<1)+(x<<3)+(c^48);
    }while(isdigit(c=getchar()));
    return f?-x:x;
}
struct Edge{
    int t,w,next;
};
Edge e[M*10];int head[M],tot=1;
int n,m,s,t,flow[M],last[M];
int vis[M][M];
inline void add(int f,int t,int w){
    e[++tot].t=t;
    e[tot].w=w;
    e[tot].next=head[f];
    head[f]=tot;
}
bool Find(){
    memset(last,-1,sizeof(last));
    flow[s]=inf;
    queue<int> q;
    q.push(s);
    while(!q.empty()){
        int x=q.front();
        q.pop();
        if(x==t) break;
        for(int i=head[x];i;i=e[i].next){
            int v=e[i].t,w=e[i].w;
            if(w>0&&last[v]==-1){
                last[v]=i;
                flow[v]=min(flow[x],w);
                q.push(v);
            }
        }
    }
    return last[t]!=-1;
}
int EK(){
    int maxflow=0;
    while(Find()){
        maxflow+=flow[t];
        for(int i=t;i!=s;i=e[last[i]^1].t){
            e[last[i]].w-=flow[t];
            e[last[i]^1].w+=flow[t];
        }
    }
    return maxflow;
}
signed main(){
   
    n=read();m=read();s=read();t=read();
    for(int i=1;i<=m;i++){
        int u=read(),v=read(),w=read();
        if(!vis[u][v]){
            add(u,v,w);
            vis[u][v]=tot;
            add(v,u,0);
        }
        else{
            e[vis[u][v]].w+=w;
        }
    }
    printf("%lld\n",EK());
    return 0;
}

Dinic算法

先BFS划出分层图,每次DFS找增广路时都往下一层增广
当前弧优化:记录每个点起始遍历的边,保证每个点起始的每条边只被遍历一次

复杂度\(O(n^2m)\)

点击查看代码
#include <bits/stdc++.h>
#define int long long
#define endl putchar('\n')
using namespace std;
const int M = 1e4+10;
const int inf = 2147483647;
typedef long long ll;
inline int read(){
    int x=0,f=0;char c=getchar();
    while(!isdigit(c)){
        if(c=='-') f=1;c=getchar();
    }
    do{
        x=(x<<1)+(x<<3)+(c^48);
    }while(isdigit(c=getchar()));
    return f?-x:x;
}
struct Edge{
    int t,w,next;
};
Edge e[M<<1];int head[M],tot=1;
int n,m,s,t,lev[M],cur[M];
inline void add(int f,int t,int w){
    e[++tot]=(Edge){t,w,head[f]};head[f]=tot;
    e[++tot]=(Edge){f,0,head[t]};head[t]=tot;
}
bool bfs(){
    memset(lev,-1,sizeof(lev));
    memcpy(cur,head,sizeof(head));
    lev[s]=0;
    queue<int> q;
    q.push(s);
    while(!q.empty()){
        int x=q.front();q.pop();
        for(int i=head[x];i;i=e[i].next){
            int v=e[i].t,w=e[i].w;
            if(w>0&&lev[v]==-1){
                lev[v]=lev[x]+1;
                q.push(v);
            }
        }
    }
    return lev[t]!=-1;
}
int dfs(int u,int flow){
    if(flow<=0||u==t) return flow;
    int rest=flow;
    for(int i=cur[u];i&&rest;i=e[i].next){
        cur[u]=i;
        int v=e[i].t,w=e[i].w;
        if(w>0&&lev[v]==lev[u]+1){
            int res=dfs(v,min(rest,w));
            rest-=res;
            e[i].w-=res;
            e[i^1].w+=res;
        }
    }
    return flow-rest;
}
int dinic(){
    int ans=0;
    while(bfs()) ans+=dfs(s,inf);
    return ans;
}
signed main(){
    n=read();m=read();s=read();t=read();
    for(int i=1;i<=m;i++){
        int u=read(),v=read(),w=read();
        add(u,v,w);
    }
    printf("%lld\n",dinic());
    return 0;
}

费用流

每条边多了一个费用\(c\),每流过1单位流量就花费\(c\)

最小费用最大流:在保证是最大流的前提下使费用最小。

spfa费用流

类似EK算法,但是把增广部分改成了spfa,每次增广更新最短路,每个流量为flow的增广路会新产生 \(flow*dis[t]\) 的费用

点击查看代码
#include <bits/stdc++.h>
#define endl putchar('\n')
using namespace std;
const int M = 5e3+10;
const int inf = 2147483647;
inline int read(){
    int x=0,f=0;char c=getchar();
    while(!isdigit(c)){
        if(c=='-') f=1;c=getchar();
    }
    do{
        x=(x<<1)+(x<<3)+(c^48);
    }while(isdigit(c=getchar()));
    return f?-x:x;
}
struct Edge{
    int t,w,c,next;
};
Edge e[M*40];int head[M],tot=1;
int n,m,s,t,ans1,ans2,dis[M],flow[M],last[M],inq[M];
queue<int> q;
inline void add(int f,int t,int w,int c){
    e[++tot]=(Edge){t,w,c,head[f]};head[f]=tot;
    e[++tot]=(Edge){f,0,-c,head[t]};head[t]=tot;
}
bool spfa(){
    memset(dis,0x3f,sizeof(dis));
    memset(inq,0,sizeof(inq));
    memset(last,-1,sizeof(last));
    dis[s]=0;
    flow[s]=inf;
    q.push(s);
    while(!q.empty()){
        int x=q.front();q.pop();
        inq[x]=0;
        for(int i=head[x];i;i=e[i].next){
            int v=e[i].t,w=e[i].w,c=e[i].c;
            if(w>0&&dis[v]>dis[x]+c){
                dis[v]=dis[x]+c;
                last[v]=i;
                flow[v]=min(flow[x],w);
                if(!inq[v]){
                    inq[v]=1;
                    q.push(v);
                }
            }
        }
    }
    return last[t]!=-1;
}
void EK(){
    while(spfa()){
        ans1+=flow[t];
        ans2+=flow[t]*dis[t];
        for(int i=t;i!=s;i=e[last[i]^1].t){
            e[last[i]].w-=flow[t];
            e[last[i]^1].w+=flow[t];
        }
    }
}
int main(){
    n=read();m=read();s=read();t=read();
    for(int i=1;i<=m;i++){
        int u=read(),v=read(),w=read(),c=read();
        add(u,v,w,c);
    }
    EK();
    printf("%d %d\n",ans1,ans2);
    return 0;
}

上下界网络流

每条边有了最低流量限制 \(L\) 和最高流量限制 \(R\)

无源汇上下界可行流

先假设所有边的流量都是他们的流量下界 \(L\),上界相应变成 \(R-L\),此时不一定满足流量守恒,对于一个点 \(i\),设它的流入量为 \(x\),流出量为 \(y\),建立虚拟源点 \(ss\) 和虚拟汇点 \(tt\)。若 \(y>x\),也就是要附加流要流进来更多,则要给多的流量找去路,连一条从 \(i\) 到 \(tt\) 容量为 \(x-y\) 的边,同理若 \(x>y\),则连一条 \(ss\) 到 \(i\) 的容量为 \(y-x\) 的边

若 \(s\) 连出去的边可以满流则说明存在可行流

此时每条边流量为每条边的反向边容量加上它的下界

有源汇上下界可行流

连一条从 \(t\) 到 \(s\) 下界为0上界为 \(inf\) 的边即可

有源汇上下界最大流

先求一遍可行流 \(flow1\),删去 \(t\) 到 \(s\) 的边后在残量网络上跑从\(s\) 到 \(t\) 的最大流 \(flow2\),答案即为 \(flow1+flow2\)

有源汇上下界最小流

同上,但最后改成跑从 \(t\) 到 \(s\) 的最大流,答案为 \(flow1-flow2\)

标签:last,增广,int,flow,网络,笔记,学习,read,流量
From: https://www.cnblogs.com/blue-tsg/p/17020206.html

相关文章

  • 学习笔记:MIn_25筛
    Min_25筛学习笔记叫这个名字是因为这是\(Min\_25\)神犇发明的,主要用到的思想是对于质数和合数分开计算。适用范围对于一个积性函数\(f(x)\)求解:\[\sum_{i=1}^{n......
  • PXE高效批量网络装机
    一、PXE基础知识1、PXE使用条件:客户机与PXE服务器必须在同一交换机上,服务器可以分配ip地址给客户机,客户机内存必须大于2G优点规模化:同时装配多台服务器自动化:安装系统,配......
  • MySql学习笔记--基础篇02
    约束外键--父表和子表,如果要删除父表的记录时,会判断子表是否存在关联关系,如果存在不予删除  多表关系一对多,在此表中建立外键关联主表的主键多对多,建立第三张中......
  • 学习重点,选择器
    ​ 【1】代码:<!DOCTYPEhtml><html><head><metacharset="UTF-8"><title></title><styletype="text......
  • 学习重点,选择器
    ​ 【1】代码:<!DOCTYPEhtml><html><head><metacharset="UTF-8"><title></title><styletype="text......
  • 狂神说Java(零基础) Java入门笔记
    1.Java帝国的诞生​1972年C诞生,比1995年诞生的Java早了20多年。C贴近硬件,运行极快,效率极高,用于操作系统、编译器、数据库、网络系统等,但是在指针和内存管理方面,常常让程序......
  • 生成对抗网络GANs的用途
    简介如果说目前深度学习最火,应用最多的领域,莫过于GAN--GenerativeAdversarialNetwork,翻译过来就是生成对抗网络,单单从名字上看,你会觉得它就是一个生成模型,看起来就是用于......
  • 【网络】网络发展,网络协议,网络传输流程,地址管理
    1.计算机网络背景1.1网络发展计算机体系结构本质也可以被看做是一个小型网络。计算机与计算机之间也是用“线”连接起来的。与其说两台计算机通信,本质上其实也是通过“线”......
  • MAUI Blazor学习2-创建移动客户端Razor页面
     MAUIBlazor学习2-创建移动客户端Razor页面 MAUIBlazor系列目录MAUIBlazor学习1-移动客户端Shell布局-SunnyTrudeau-博客园(cnblogs.com)  为了适配......
  • Git和Maven的学习笔记
    Git1、Git简介Git是一个免费的、开源的分布式版本控制系统,可以快速高效地处理从小型到大型的各种项目。Git易于学习,占地面积小,性能极快。它具有廉价的本地库,方便的......