网络流算法

参考:网络流算法详解-CSDN博客

网络流(二) 最小费用最大流问题-CSDN博客

[网络流]学习笔记:一次理解网络流!-CSDN博客

最大流算法:

主要思想就是每次找到一条增广路径,直到网络中不存在增广路径时,就找到了最大流。

最大流算法证明(AI回答):

最大流最小割定理证明
一、弱对偶性证明

对于任意可行流 ff 和任意割 (S,T),其流量 ∣f∣ 不超过割的容量c(S,T),即:

∣f∣≤∑u∈S,v∈Tc(u,v)

论证步骤‌:

  1. 流量分解‌:流的流量 ∣f∣ 可分解为割 (S,T) 中正向边流量之和减反向边流量之和,即:∣f∣=∑u∈S,v∈Tf(u,v)−∑u∈T,v∈Sf(u,v)
  2. 容量约束‌:由于 f(u,v)≤c(u,v) 且反向边流量 f(v,u)≥0,可得:∣f∣≤∑u∈S,v∈Tc(u,v)因此,最大流的值必然不超过最小割的容量
最大流算法证明
1. 最大流存在的必要条件
  • 任意流的流量不超过任意割的容量‌:
    对于割 (S,T)(,其容量 c(S,T)=∑u∈S,v∈Tc(u,v),而流 f的流量满足 ∣f∣≤c(S,T)。
2. 算法终止时的流为最大流
  • 构造割 (S,T):当残留网络中无增广路径时,定义集合 S 包含从源点可达的所有节点,T=V∖S。
  • 割的流量等于容量‌:此时 S 到 T 的所有边流量饱和,T 到 S 的边流量为零,因此 ∣f∣=c(S,T)∣。
  • 最小割的存在性‌:由 ∣f∣≤c(S,T) 且构造的割容量等于当前流量,知当前流即为最大流

最小费用最大流算法

主要思想就是每次找到一条费用最小的增广路径,直到找不到网络中的增广路径时,就找到了最大流的最小费用。

最小费用最大流算法证明(AI回答)

1. 初始状态的正确性
  • 零流的合法性‌:初始流 f=0,费用为0,显然满足费用最优性。
2. 归纳假设

假设当前流 f 是所有流量为 ∣f∣ 的可行流中费用最小的。需证明:通过增广一条费用最小的路径 P,得到流 f′,则 f′f′ 是所有流量为 ∣f∣+Δ的流中费用最小的。

3. 关键引理

引理‌:若每次增广的是残余网络中费用最小的路径,则新流的费用仍为当前流量下的最小值。

证明‌(反证法):

  • 假设存在更优流‌:假设存在另一流 f′′,满足 ∣f′′∣=∣f′∣ 且 cost(f′′)
  • 流分解定理‌:将流 f′′−f 分解为若干简单路径和环的叠加。由于 cost(f′′−f)
  • 矛盾‌:这与算法每次选择费用最小的路径增广矛盾,因此假设不成立。
4. 负权边的处理
  • 反向边费用补偿‌:反向边费用为 −a(u,v),确保增广路径费用计算的正确性。例如,若从 u→v原有流量 f(u,v),则反向边 v→u允许回退流量,并补偿费用。
  • SPFA算法的必要性‌:由于反向边可能引入负权边,需使用SPFA算法寻找最短路(Dijkstra需结合势函数才可处理负权)。
5. 终止条件

当残余网络中不存在源点到汇点的增广路径时,当前流为最大流。根据归纳法,此时的费用也为最小值

复杂度分析
  • 时间复杂度‌:依赖于增广次数和最短路径算法复杂度。若采用SPFA,复杂度为 O(F⋅VE)(F 为最大流量,E 和 V 为边数和顶点数)。
  • 势函数优化‌:通过势函数将边权调整为非负,可使用Dijkstra算法减少时间复杂度至 O(F⋅(E+Vlog⁡V))

总结:

最大流算法和最小费用算法都有贪心的思想,每次迭代都会离最大流网络更近一步。网络流的问题难度主要在抽象建模,很多问题同网络流联系起来并不那边直观。那些可以分成2组节点相互连边的问题通常可以转换为网络流解决,比如二分图匹配。因为引入了反向边,每次找增广路时需要更新正向边和反向边的流量,就需要快查找到增广路径上的边。所以在网络流算法中一般用链式前向星(邻接矩阵不支持两点间连多条边,邻接表不支持快速定位到对应的边)这种数据结构表示图比较高效方便。需要注意反向边的容量为0,花费为负。

最大流算法代码实现

题目:E. Soldier and Traveling 

 https://codeforces.com/problemset/problem/546/E

思路:n个值建对应n个节点,分配给n个位置对应n个节点 。原点到n个节点连边的流量为初始值,n个位置到终点的流量为期望值。需要检测最终的残余网络,输出边的分配情况

Edmonds-Karp算法

复杂度O(V*E^2),证明

https://www.zhihu.com/question/65133832/answer/2306908696

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.StringTokenizer;

public class Main {
    public static void main(String args[]) {new Main().run();}

    FastReader in = new FastReader();
    PrintWriter out = new PrintWriter(System.out);
    void run() {
        work();
        out.flush();
    }
    long mod=998244353;
    long inf=Long.MAX_VALUE/3;
    long gcd(long a,long b) {
        return a==0?b:gcd(b%a,a);
    }
    Edge[] edges;
    int[] head;
    int[] route;
    int maxn=220;
    int S,T;
    int cur;
    void addEdge(int s,int e,int flow,int cap){
        Edge edge=new Edge(s,e,flow,cap,head[s]);
        edges[cur]=edge;
        head[s]=cur;
        cur++;
    }
    void work(){
        head=new int[maxn];
        Arrays.fill(head, -1);
        route=new int[maxn];
        edges=new Edge[maxn*200];
        int n=ni(),m=ni();
        int[] A=nia(n);
        int[] B=nia(n);
        int sum=0,sum1=0;
        for(int i=0;i queue=new LinkedList<>();
            queue.add(S);
            boolean[] vis=new boolean[maxn];
            int[] rec=new int[maxn];//当前增广路增加的流量
            Arrays.fill(rec,999999999);
            Arrays.fill(route,-1);
            vis[S]=true;
            while(queue.size()>0){
                Integer node = queue.poll();
                for(int i=head[node];i!=-1;i=edges[i].next){
                    Edge edge = edges[i];
                    if(!vis[edge.to]&&edge.flow[] ng(int n, int m) {
        ArrayList[] graph=(ArrayList[])new ArrayList[n];
        for(int i=0;i();
        }
        for(int i=1;i<=m;i++) {
            int s=in.nextInt()-1,e=in.nextInt()-1;
            graph[s].add(e);
            graph[e].add(s);
        }
        return graph;
    }

    private ArrayList[] ngw(int n, int m) {
        ArrayList[] graph=(ArrayList[])new ArrayList[n];
        for(int i=0;i();
        }
        for(int i=1;i<=m;i++) {
            long s=in.nextLong()-1,e=in.nextLong()-1,w=in.nextLong();
            graph[(int)s].add(new long[] {e,w});
            graph[(int)e].add(new long[] {s,w});
        }
        return graph;
    }

    private int ni() {
        return in.nextInt();
    }

    private long nl() {
        return in.nextLong();
    }
    private double nd() {
        return in.nextDouble();
    }
    private String ns() {
        return in.next();
    }

    private long[] na(int n) {
        long[] A=new long[n];
        for(int i=0;i'9'){
                    b=input.read();
                }
                while(b>='0'&&b<='9'){
                    ret=ret*10+(b-'0');
                    b=input.read();
                }
                return ret;
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        return Long.parseLong(next());
    }

    public double nextDouble()
    {
        return Double.parseDouble(next());
    }
}
dinic算法

找增广路径时每次不需要从头开始搜索,复杂度O(V^2*E),证明讨论 - 力扣(LeetCode)

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.StringTokenizer;

public class Main {
    public static void main(String args[]) {new Main().run();}

    FastReader in = new FastReader();
    PrintWriter out = new PrintWriter(System.out);
    void run() {
        work();
        out.flush();
    }
    long mod=998244353;
    int inf=999999999;
    long gcd(long a,long b) {
        return a==0?b:gcd(b%a,a);
    }
    Edge[] edges;
    int[] head;
    int[] route;
    int[] deep;
    int maxn=220;
    int S,T;
    int cur;
    void addEdge(int s,int e,int flow,int cap){
        Edge edge=new Edge(s,e,flow,cap,head[s]);
        edges[cur]=edge;
        head[s]=cur;
        cur++;
    }
    void work(){
        head=new int[maxn];
        deep=new int[maxn];
        Arrays.fill(head, -1);
        route=new int[maxn];
        edges=new Edge[maxn*200];
        int n=ni(),m=ni();
        int[] A=nia(n);
        int[] B=nia(n);
        int sum=0,sum1=0;
        for(int i=0;i queue=new LinkedList<>();
        queue.add(S);
        Arrays.fill(deep,-1);
        deep[S]=0;
        while(queue.size()>0){
            Integer node = queue.poll();
            for(int i=head[node];i!=-1;i=edges[i].next){
                Edge edge = edges[i];
                if(deep[edge.to]==-1&&edge.flow[] ng(int n, int m) {
        ArrayList[] graph=(ArrayList[])new ArrayList[n];
        for(int i=0;i();
        }
        for(int i=1;i<=m;i++) {
            int s=in.nextInt()-1,e=in.nextInt()-1;
            graph[s].add(e);
            graph[e].add(s);
        }
        return graph;
    }

    private ArrayList[] ngw(int n, int m) {
        ArrayList[] graph=(ArrayList[])new ArrayList[n];
        for(int i=0;i();
        }
        for(int i=1;i<=m;i++) {
            long s=in.nextLong()-1,e=in.nextLong()-1,w=in.nextLong();
            graph[(int)s].add(new long[] {e,w});
            graph[(int)e].add(new long[] {s,w});
        }
        return graph;
    }

    private int ni() {
        return in.nextInt();
    }

    private long nl() {
        return in.nextLong();
    }
    private double nd() {
        return in.nextDouble();
    }
    private String ns() {
        return in.next();
    }

    private long[] na(int n) {
        long[] A=new long[n];
        for(int i=0;i'9'){
                    b=input.read();
                }
                while(b>='0'&&b<='9'){
                    ret=ret*10+(b-'0');
                    b=input.read();
                }
                return ret;
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        return Long.parseLong(next());
    }

    public double nextDouble()
    {
        return Double.parseDouble(next());
    }
}

最小费用最大流算法代码实现:

题目:863F - Almost Permutation

https://codeforces.com/contest/863/problem/F

该题的关键在于构造图,构造1个节点(下标0)代表原点S,n个节点(下标1到n)代表从1到n的n个值,n个节点(下标n+1到2n)代表从1到n的n个位置,1个终点T。其中原点到每个下标1到n的点都连50条边,费用为2*j-1,(j表示第j条边),下标1到n的点根据题目条件限制分别连上多条边到下标n+1到2*n的点,每个下标n+1到2*n的点在连一条边到终点,其中流量都为1。

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.StringTokenizer;

public class Main {
    public static void main(String args[]) {new Main().run();}

    FastReader in = new FastReader();
    PrintWriter out = new PrintWriter(System.out);
    void run() {
        work();
        out.flush();
    }
    long mod=998244353;
    long inf=Long.MAX_VALUE/3;
    long gcd(long a,long b) {
        return a==0?b:gcd(b%a,a);
    }
    Edge[] edges;
    int[] head;
    int[] route;
    int maxn=120;
    int S,T;
    int cur;
    void addEdge(int s,int e,int flow,int cap,int cost){
        Edge edge=new Edge(s,e,flow,cap,cost,head[s]);
        edges[cur]=edge;
        head[s]=cur;
        cur++;
    }
    void work(){
        int n=ni();
        edges=new Edge[maxn*100];
        head=new int[maxn];
        Arrays.fill(head,-1);
        route=new int[maxn];
        boolean[][] rec=new boolean[n+1][n+1];
        for(int q=ni();q>0;q--){
            int t=ni(),l=ni(),r=ni(),v=ni();
            if(t==1){
                for(int i=l;i<=r;i++){
                    for(int j=1;j queue=new LinkedList<>();
        queue.add(S);
        boolean[] vis=new boolean[maxn];
        int[] rec=new int[maxn];
        Arrays.fill(rec,999999999);
        Arrays.fill(route,-1);
        rec[S]=0;
        while(queue.size()>0){
            Integer node = queue.poll();
            vis[node]=false;
            for(int i=head[node];i!=-1;i=edges[i].next){
                Edge edge = edges[i];
                if(rec[edge.to]>rec[node]+edge.cost&&edge.flow[] ng(int n, int m) {
        ArrayList[] graph=(ArrayList[])new ArrayList[n];
        for(int i=0;i();
        }
        for(int i=1;i<=m;i++) {
            int s=in.nextInt()-1,e=in.nextInt()-1;
            graph[s].add(e);
            graph[e].add(s);
        }
        return graph;
    }

    private ArrayList[] ngw(int n, int m) {
        ArrayList[] graph=(ArrayList[])new ArrayList[n];
        for(int i=0;i();
        }
        for(int i=1;i<=m;i++) {
            long s=in.nextLong()-1,e=in.nextLong()-1,w=in.nextLong();
            graph[(int)s].add(new long[] {e,w});
            graph[(int)e].add(new long[] {s,w});
        }
        return graph;
    }

    private int ni() {
        return in.nextInt();
    }

    private long nl() {
        return in.nextLong();
    }
    private double nd() {
        return in.nextDouble();
    }
    private String ns() {
        return in.next();
    }

    private long[] na(int n) {
        long[] A=new long[n];
        for(int i=0;i'9'){
                    b=input.read();
                }
                while(b>='0'&&b<='9'){
                    ret=ret*10+(b-'0');
                    b=input.read();
                }
                return ret;
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        return Long.parseLong(next());
    }

    public double nextDouble()
    {
        return Double.parseDouble(next());
    }
}

你可能感兴趣的:(Codeforces,算法)