• 最大流与最小费用最大流简略版)


    1.最大流

    由源点 s s s到汇点 t t t的最大流量。
    图的最大流 ≤ \leq 将图进行割边的流量(任意割)

    证明最大流等于最小割:

    简略版)
    对于一张图网络,假设开始是一个连通图且源点 s s s和汇点 t t t都在一个集合里,设图网络的最大流为 F F F,那么 F F F等于从源点 s s s流出的流量(假设源点有无限的流量)且 F F F等于从汇点 t t t流入的流量,当对连通图进行割边后集合会被分成两个集合,源点与汇点分别在两个集合中,此时源点流出的流量等于汇点流入的流量等于割边的流量。即图网络的最大流等于最小割。

    思想

    发现每次从 s s s t t t进行对残量网络的一条路径减去路径的最小值,当沿残量网络无法到达 t t t时,即构造出了割边。复杂度为 O ( n 2 m ) O(n^2m) O(n2m),但实际远远达到不了这里。

    代码

    #include <bits/stdc++.h>
    using namespace std;
    using ll = long long ;
    const int N = 5e3+10 ;
    ll e[N<<1],ne[N<<1],w[N<<1],h[N<<1],k;
    void add(int a,int b,int c){
        e[++k]=b,ne[k]=h[a],w[k]=c,h[a]=k;
    }
    ll dep[N];
    bool bfs(int s,int t){
        memset(dep,0,sizeof dep);
        dep[s]=1;
        queue<int>q;
        q.push(s);
        while(!q.empty()){
            int t=q.front();q.pop();
            for(int i=h[t];i;i=ne[i]){
                int j=e[i];
                if(w[i] and !dep[j]){
                    dep[j]=dep[t]+1;
                    q.push(j);
                }
            }
        }
        return dep[t];
    }
    ll dfs(int s,int t,ll in){
        if(s==t)return in;
        ll out=0;
        for(int i=h[s];i;i=ne[i]){
            int j=e[i];
            if(w[i] and dep[j]==dep[s]+1){
                ll x=dfs(j,t,min(in,w[i]));
                w[i]-=x;
                w[i^1]+=x;
                out+=x;
                in-=x;
            }
        }
        if(out==0)dep[s]=0;
        return out;
    }
    ll dinic(int s,int t){
        ll ans=0;
        while(bfs(s,t))ans+=dfs(s,t,1e18);
        return ans;
    }
    template<class Tp=int>
    Tp read(){
        char ch=getchar();bool f=1;Tp x=0;
        while(!isdigit(ch)){
            if(ch=='-')f=false;
            ch=getchar();
        }
        while(isdigit(ch)){
            x=(x<<3)+(x<<1)+(ch^48);
            ch=getchar();
        }
        return f?x:-x;
    }
    template<class Tp=int>
    void print(Tp n){
        if(n>9)print(n/10);
        putchar(n%10|'0');
    }
    int main()
    {
        k=1;
        ios::sync_with_stdio(false);
        int n,m,s,t;
        n=read(),m=read(),s=read(),t=read();
        for(int i=0,a,b,c;i<m;i++){
            a=read(),b=read(),c=read();
            add(a,b,c);
            add(b,a,0);
        }
        print<ll>(dinic(s,t));
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79

    2.最小费用最大流

    很明显是在最小费用的基础上求最大流。

    思想

    每次使用dijskra或spfa以单位费用最小找到从 s s s t t t的最小费用路径,和构造最大流一样构造割边即可,复杂度为 O ( m n l o g 2 n ) O(mnlog_2n) O(mnlog2n) O ( n 2 m ) O(n^2m) O(n2m)

    代码

    #include <bits/stdc++.h>
    using namespace std;
    using ll = long long ;
    const int N = 5e4+10 ;
    const ll inf = 1e10 ;
    ll e[N<<1],ne[N<<1],h[N<<1],w[N<<1],c[N<<1];
    int k=1;
    void add(int a,int b,int c,int d){
        e[++k]=b,ne[k]=h[a],h[a]=k;
        w[k]=c,::c[k]=d;
    }
    ll dis[N];
    bool vis[N];
    int dep[N];
    ll flow[N];
    int pre[N],last[N];
    bool spfa(int s,int t){
        memset(dis,0x7f,sizeof dis);
        memset(flow,0x7f,sizeof flow);
        memset(pre,-1,sizeof pre);
        memset(vis,0,sizeof vis);
        queue<int>q;
        q.push(s);
        pre[s]=0;
        vis[s]=true;
        dis[s]=0;
        while(!q.empty()){
            int t=q.front();q.pop();
            vis[t]=false;
            for(int i=h[t];i;i=ne[i]){
                int j=e[i];
                if(w[i]&&dis[j]>dis[t]+c[i]){
                    last[j]=i;
                    dis[j]=dis[t]+c[i];
                    pre[j]=t;
                    flow[j]=min(flow[t],w[i]);
                    if(!vis[j]){
                        q.push(j);
                        vis[j]=true;
                    }
                }
            }
        }
        return pre[t]!=-1;
    }
    void dinic(int s,int t){
        ll F=0,D=0;
        while(spfa(s,t)){
            int x=t;
            F+=flow[t];
            D+=flow[t]*dis[t];
            while(x!=s){
                w[last[x]]-=flow[t];
                w[last[x]^1]+=flow[t];
                x=pre[x];
            }
        }
        cout<<F<<" "<<D;
    }
    int main()
    {
        ios::sync_with_stdio(false);
        int n,m,s,t;
        cin>>n>>m>>s>>t;
        for(int i=0,u,v,w,c;i<m;i++){
            cin>>u>>v>>w>>c;
            add(u,v,w,c);
            add(v,u,0,-c);
        }
        dinic(s,t);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
  • 相关阅读:
    想去银行测试?那这套题目你必须要会
    qt pro如何增加自定义值为的字符串的宏
    UNIX环境高级编程-第三章
    资源管理平台头部导航栏(1+X Web前端开发初级 例题)
    游戏开发应该关注质量而不是数量
    【AIGC核心技术剖析】扩大富有表现力的人体姿势和形状估计SMPLer-X模型
    10.Oracle的同义词与序列
    Xcode 常见错误
    Redis05:Redis高级部分
    【英雄哥六月集训】第 29天: 分而治之
  • 原文地址:https://blog.csdn.net/ftrghujhgf/article/details/125418291