网络流进阶

Posted hjmmm

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了网络流进阶相关的知识,希望对你有一定的参考价值。

先把三道例题扔在这里吧qvq
无源汇有上下界可行流
有源汇有上下界最大流
有源汇有上下界最小流

无源汇有上下界可行流

首先默认把下界跑满 但显然这样是不满足流量平衡的
所以建立超级源S,超级汇T
把所有“入不敷出”的点向T连一条大小为 出流-入流 的边
把S向所有入流大的点连一条大小为 入流-出流 的边
此时跑一遍网络流 如果与S相连的边都满流了 那么T也满流了
整个图即可行 否则不可行

#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <queue>
using namespace std;
const int N = 205;
const int M = 1e6;
const int inf = 0x3f3f3f3f;
struct Edge{
    int v, f, next;
}edge[M];
int head[N], cur[N], esize = -1;
inline void addedge(int x, int y, int z){
    //printf("%d %d %d
", x, y, z);
    edge[++esize] = (Edge){y, z, head[x]};
    head[x] = esize;
    edge[++esize] = (Edge){x, 0, head[y]};
    head[y] = esize;
}

int n, m, S, T, cnt, mf;
int low[M], upp[M], in[N], out[N];
int dis[N], fro;
queue<int> que;
bool bfs(){
    memset(dis, 0, sizeof(dis));
    que.push(S); dis[S] = 1;
    while(!que.empty()){
        fro = que.front(); que.pop();
        for(int i = head[fro], vv; ~i; i = edge[i].next){
            vv = edge[i].v;
            if(!dis[vv] && edge[i].f > 0){
                que.push(vv);
                dis[vv] = dis[fro] + 1;
            }
        }
    }
    return dis[T];
}
int dfs(int x, int rest){
    if(!rest || x == T) return rest;
    for(int& i = cur[x], vv; ~i; i = edge[i].next){
        vv = edge[i].v;
        if(dis[vv] == dis[x] + 1 && edge[i].f > 0){
            int d = dfs(vv, min(rest, edge[i].f));
            if(d > 0){
                edge[i].f -= d;
                edge[i ^ 1].f += d;
                return d; 
            }
        }
    }
    return 0;
}

void dinic(){
    mf = 0;
    while(bfs()){
        //for(int i = 1; i <= T; ++i) printf("%d %d
", i, dis[i]); 
        for(int i = 1; i <= T; ++i) cur[i] = head[i];
        mf += dfs(S, inf);
        //printf("mf = %d
", mf);
    }
} 

int main(){
    memset(head, -1, sizeof(head));
    scanf("%d%d", &n, &m);
    for(int i = 1, x, y; i <= m; ++i){
        scanf("%d%d%d%d", &x, &y, &low[i], &upp[i]);
        addedge(x, y, upp[i] - low[i]);
        out[x] += low[i];
        in[y] += low[i];
    }
    S = n + 1, T = n + 2;
    for(int i = 1; i <= n; ++i){
        if(in[i] > out[i]){
            addedge(S, i, in[i] - out[i]);
            cnt += in[i] - out[i]; 
        }
        else if(out[i] > in[i]) addedge(i, T, out[i] - in[i]);
    }
    dinic();
    if(mf == cnt){
        printf("YES
");
        for(int i = 1; i <= m; ++i){
            printf("%d
", low[i] + edge[(i << 1) - 1].f);
        }
    } 
    else printf("NO
");
    return 0;   
}

有源汇有上下界最大流

在原图上再跑一次最大流就好啦

#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <queue>
using namespace std;
const int N = 205;
const int M = 1e6;
const int inf = 0x3f3f3f3f;
struct Edge{
    int v, f, next;
}edge[M];
int head[N], cur[N], esize = -1;
inline void addedge(int x, int y, int z){
    //printf("%d %d %d
", x, y, z);
    edge[++esize] = (Edge){y, z, head[x]};
    head[x] = esize;
    edge[++esize] = (Edge){x, 0, head[y]};
    head[y] = esize;
}

int n, m, S, T, SS, TT, cnt, mf;
int low[M], upp[M], d[N];
int dis[N], fro;
queue<int> que;
bool bfs(int s, int t){
    memset(dis, 0, sizeof(dis));
    que.push(s); dis[s] = 1;
    while(!que.empty()){
        fro = que.front(); que.pop();
        for(int i = head[fro], vv; ~i; i = edge[i].next){
            vv = edge[i].v;
            if(!dis[vv] && edge[i].f > 0){
                que.push(vv);
                dis[vv] = dis[fro] + 1;
            }
        }
    }
    //printf("%d
", dis[T]);
    return dis[t];
}
int dfs(int x, int rest, int t){
    //  printf("%d %d
", x, rest);
    if(!rest || x == t) return rest;
    for(int& i = cur[x], vv; ~i; i = edge[i].next){
        vv = edge[i].v;
        if(dis[vv] == dis[x] + 1 && edge[i].f > 0){
            int d = dfs(vv, min(rest, edge[i].f), t);
            if(d > 0){
                //printf("%d
", d);
                edge[i].f -= d;
                edge[i ^ 1].f += d;
                return d; 
            }
        }
    }
    return 0;
}

void dinic(int s, int t){
    mf = 0;
    while(bfs(s, t)){
        //for(int i = 1; i <= T; ++i) printf("%d %d
", i, dis[i]); 
        memcpy(cur, head, sizeof(cur));
        mf += dfs(s, inf, t);
        //printf("mf = %d
", mf);
    }
} 

int main(){
    memset(head, -1, sizeof(head));
    scanf("%d%d%d%d", &n, &m, &S, &T);
    for(int i = 1, x, y; i <= m; ++i){
        scanf("%d%d%d%d", &x, &y, &low[i], &upp[i]);
        addedge(x, y, upp[i] - low[i]);
        d[x] -= low[i];
        d[y] += low[i];
    }
    //printf("*");
    SS = n + 1, TT = n + 2;
    for(int i = 1; i <= n; ++i){
        if(d[i] > 0){
            addedge(SS, i, d[i]);
            cnt += d[i]; 
        }
        else if(d[i] < 0) addedge(i, TT, -d[i]);
    }
    addedge(T, S, inf);
    int rec = esize;
    
    dinic(SS, TT);
    
    if(mf != cnt){
        printf("please go home to sleep
");
        return 0;
    } 
    
    head[S] = edge[head[S]].next;
    head[T] = edge[head[T]].next;
    dinic(S, T); mf += edge[rec].f;
    printf("%d", mf);
    return 0;   
}

有源汇有上下界最小流

1.从SS到TT跑最大流
2.从T到S连一条大小为INF的边
3.从SS到TT跑最大流
4.最后T到S的流量就是最小流

#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <queue>
using namespace std;
const int N = 5e4 + 9;
const int M = 1e6;
const int inf = 0x3f3f3f3f;
struct Edge{
    int v, f, next;
}edge[M];
int head[N], cur[N], esize = -1;
inline void addedge(int x, int y, int z){
    //printf("%d %d %d
", x, y, z);
    edge[++esize] = (Edge){y, z, head[x]};
    head[x] = esize;
    edge[++esize] = (Edge){x, 0, head[y]};
    head[y] = esize;
}

int n, m, S, T, SS, TT, cnt, mf;
int low[M], upp[M], d[N];
int dis[N], fro;
queue<int> que;
bool bfs(int s, int t){
    memset(dis, 0, sizeof(dis));
    que.push(s); dis[s] = 1;
    while(!que.empty()){
        fro = que.front(); que.pop();
        for(int i = head[fro], vv; ~i; i = edge[i].next){
            vv = edge[i].v;
            if(!dis[vv] && edge[i].f > 0){
                que.push(vv);
                dis[vv] = dis[fro] + 1;
            }
        }
    }
    //printf("%d
", dis[T]);
    return dis[t];
}
int dfs(int x, int rest, int t){
    //  printf("%d %d
", x, rest);
    if(!rest || x == t) return rest;
    int tmp = 0;
    for(int& i = cur[x], vv; ~i; i = edge[i].next){
        vv = edge[i].v;
        if(dis[vv] == dis[x] + 1 && edge[i].f > 0){
            int d = dfs(vv, min(rest - tmp, edge[i].f), t);
            edge[i].f -= d;
            edge[i ^ 1].f += d;
            tmp += d;
            if(tmp == rest) return tmp;
        }
    }
    return tmp;
}

void dinic(int s, int t){
    while(bfs(s, t)){
        //for(int i = 1; i <= T; ++i) printf("%d %d
", i, dis[i]); 
        memcpy(cur, head, sizeof(cur));
        mf += dfs(s, inf, t);
        //printf("mf = %d
", mf);
    }
} 

int main(){
    memset(head, -1, sizeof(head));
    scanf("%d%d%d%d", &n, &m, &S, &T);
    for(int i = 1, x, y; i <= m; ++i){
        scanf("%d%d%d%d", &x, &y, &low[i], &upp[i]);
        addedge(x, y, upp[i] - low[i]);
        d[x] -= low[i];
        d[y] += low[i];
    }
    //printf("*");
    SS = n + 1, TT = n + 2;
    for(int i = 1; i <= n; ++i){
        if(d[i] > 0){
            addedge(SS, i, d[i]);
            cnt += d[i]; 
        }
        else if(d[i] < 0) addedge(i, TT, -d[i]);
    }
    
    dinic(SS, TT);
    
    addedge(T, S, inf);
    int rec = esize;
    
    dinic(SS, TT); 
    if(mf != cnt){
        printf("please go home to sleep
");
        return 0;
    } 
    else printf("%d", edge[rec].f);
    return 0;   
}

以上是关于网络流进阶的主要内容,如果未能解决你的问题,请参考以下文章

网络流(进阶)

[知识点]网络流进阶之对偶图

Java进阶(10) - 网络编程

我的Android进阶之旅NDK开发之在C++代码中使用Android Log打印日志,打印出C++的函数耗时以及代码片段耗时详情

Atom编辑器入门到精通 Atom使用进阶

网络流初步详解