算法笔记--最大流和最小割 && 最小费用最大流 && 上下界网络流

最大流:

给定指定的一个有向图,其中有两个特殊的点源S(Sources)和汇T(Sinks),每条边有指定的容量(Capacity),求满足条件的从S到T的最大流(MaxFlow)。

最小割:

割是网络中定点的一个划分,它把网络中的所有顶点划分成两个顶点集合S和T,其中源点s∈S,汇点t∈T,从S出发指向T的边的集合,称为割(S,T),这些边的容量之和称为割的容量。容量最小的割称为最小割。

根据最大流最小割定理,最大流等于最小割。

其他:

求最小割边的个数的方法:

①建边的时候每条边权 w = w * (E + 1) + 1;这样得到最大流 maxflow / (E + 1) ,最少割边数 maxflow % (E + 1)

②建图,得到最大流后,图中边若满流,说明该边是最小割上的边;再建图,原则:满流的边改为容量为 1 的边,未满流的边改为容量 INF 的边,然后最大流即答案

Ford-Fulkerson算法模板:

const int INF = 0x7f7f7f7f;
struct edge {
    int to, w, rev;
};
vector<edge> g[N];
bool vis[555];
void add_edge(int from, int to, int w) {
    g[from].pb((edge){to, w, g[to].size()});
    g[to].pb((edge){from, 0, g[from].size()-1});
}
//通过dfs寻找增广路
int dfs(int u, int t,int f) {
    if (u == t) return f;
    vis[u] = true;
    for (int i = 0; i < g[u].size(); i++) {
        edge &e = g[u][i];
        if(!vis[e.to] && e.w > 0) {
            int d = dfs(e.to, t, min(f, e.w));
            if(d > 0) {
                e.w -= d;
                g[e.to][e.rev].w += d;
                return d;
            }
        }
    }
    return 0;
}
int max_flow(int s, int t) {
    int flow = 0;
    while(true) {
        mem(vis, false);
        int f = dfs(s, t, INF);
        if (f == 0) return flow;
        flow += f;
    }
    return flow;
}

Dinic算法+弧优化模板:

const int INF = 0x7f7f7f7f;
int level[N], iter[N]; 
struct edge {
    int to, w, rev;
};
vector<edge>g[N];
void add_edge(int u, int v, int w) {
    g[u].pb(edge{v, w, g[v].size()});
    g[v].pb(edge{u, 0, g[u].size()-1});
}  
void bfs(int s) {
    mem(level, -1);
    queue<int>q;
    level[s] = 0;
    q.push(s);
    while (!q.empty()) {
        int u = q.front();
        q.pop();
        for (int i = 0; i < g[u].size(); i++) {
            edge e = g[u][i];
            if(e.w > 0 && level[e.to] < 0) {
                level[e.to] = level[u] + 1;
                q.push(e.to);
                
            }
        }
    }
}
int dfs(int u, int t, int f) {
    if(u == t ) return f;
    for (int &i = iter[u]; i < g[u].size(); i++) {
        edge &e = g[u][i];
        if(e.w > 0 && level[u] < level[e.to]) {
            int d = dfs(e.to, t, min(f, e.w));
            if(d > 0) {
                e.w -= d;
                g[e.to][e.rev].w +=d;
                return d;    
            }
        }
    } 
    return 0;
} 
int max_flow(int s, int t) {
    int flow = 0;
    while(true) {
        bfs(s);
        if(level[t] < 0) return flow;
        int f;
        mem(iter, 0);
        while ((f = dfs(s, t, INF)) > 0) {
            flow += f;
        }
    }
}

POJ 3281

建图:把每头牛拆成两个,然后之间建一条容量为1的边,然后把food和drink分别放在牛的两边,分别和牛建容量为1的边,然后再在food和drink左右两边分别加一个源点s和汇点t。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<vector>
#include<algorithm>
using namespace std;
#define LL long long
#define pb push_back
#define mem(a, b) memset(a, b, sizeof(a))

const int N = 1555;
const int INF = 0x7f7f7f7f;
struct edge {
    int to, w, rev;
};
vector<edge> g[N];
bool vis[555];
void add_edge(int from, int to, int w) {
    g[from].pb((edge){to, w, g[to].size()});
    g[to].pb((edge){from, 0, g[from].size()-1});
}
int dfs(int u, int t,int f) {
    if (u == t) return f;
    vis[u] = true;
    for (int i = 0; i < g[u].size(); i++) {
        edge &e = g[u][i];
        if(!vis[e.to] && e.w > 0) {
            int d = dfs(e.to, t, min(f, e.w));
            if(d > 0) {
                e.w -= d;
                g[e.to][e.rev].w += d;
                return d;
            }
        }
    }
    return 0;
}
int max_flow(int s, int t) {
    int flow = 0;
    while(true) {
        mem(vis, false);
        int f = dfs(s, t, INF);
        if (f == 0) return flow;
        flow += f;
    }
    return flow;
}
int main() {
    int n, f, d, t, _t, u;
    scanf("%d%d%d", &n, &f, &d);
    for (int i = 1; i <= n; i++) {
        add_edge(i, i+n, 1);
    }
    for (int i = 1; i <= n; i++) {
        scanf("%d%d", &t, &_t);
        while(t--) {
            scanf("%d", &u);
            add_edge(u+2*n, i, 1);
        }
        while(_t--) {
            scanf("%d", &u);
            add_edge(i+n, u+2*n+f, 1);
        }
    }
    for (int i = 1; i <= f; i++) {
        add_edge(0, i+2*n, 1);
    }
    for (int i = 1; i <= d; i++) {
        add_edge(i+2*n+f, 500, 1);
    }
    printf("%d\n", max_flow(0, 500));
    return 0;
}
View Code

 POJ 3436

建图:把每台设备拆成两个,然后之间建一条容量为设备操作台数的边,然后在所有的input里找没有1的设备,与源点s相连,在所有的output里找没有0的设备,与汇点t相连。然后设备之间如果input和output能匹配,就相连。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<vector>
#include<queue>
#include<algorithm>
using namespace std;
#define LL long long 
#define pb push_back
#define mem(a, b) memset(a, b, sizeof(a))

const int N = 66;
const int INF = 0x7f7f7f7f;
int level[N*2], iter[N*2], in[N][15], out[N][15]; 
bool vis[N][N];
struct edge {
    int to, w, rev;
};
struct node {
    int x, y, z;
}res[N*N];
vector<edge>g[N*2];
void add_edge(int u, int v, int w) {
    g[u].pb(edge{v, w, g[v].size()});
    g[v].pb(edge{u, 0, g[u].size()-1});
}  
void bfs(int s) {
    mem(level, -1);
    queue<int>q;
    level[s] = 0;
    q.push(s);
    while (!q.empty()) {
        int u = q.front();
        q.pop();
        for (int i = 0; i < g[u].size(); i++) {
            edge e = g[u][i];
            if(e.w > 0 && level[e.to] < 0) {
                level[e.to] = level[u] + 1;
                q.push(e.to);
                
            }
        }
    }
}
int dfs(int u, int t, int f) {
    if(u == t ) return f;
    for (int &i = iter[u]; i < g[u].size(); i++) {
        edge &e = g[u][i];
        if(e.w > 0 && level[u] < level[e.to]) {
            int d = dfs(e.to, t, min(f, e.w));
            if(d > 0) {
                e.w -= d;
                g[e.to][e.rev].w +=d;
                return d;    
            }
        }
    } 
    return 0;
} 
int max_flow(int s, int t) {
    int flow = 0;
    while(true) {
        bfs(s);
        if(level[t] < 0) return flow;
        int f;
        mem(iter, 0);
        while ((f = dfs(s, t, INF)) > 0) {
            flow += f;
        }
    }
}
int main() {
    int p, n, w;
    while(~ scanf("%d%d", &p, &n)) {
        for (int i = 0; i < N*2; i++) g[i].clear();
        mem(vis, false);
        for (int i = 1; i <= n; i++) {
            scanf("%d", &w);
            add_edge(i, i+n, w);
            for (int j = 1; j <= p; j++) scanf("%d", &in[i][j]);
            for (int j = 1; j <= p; j++) scanf("%d", &out[i][j]);
        } 
        for (int i = 1; i <= n; i++) {
            bool f = true, _f = true;
            for (int j = 1; j <= p; j++) if(in[i][j] == 1) f = false;
            for (int j = 1; j <= p; j++) if(!out[i][j]) _f = false;
            if(f) add_edge(0, i, INF);
            if(_f) add_edge(i+n, 120, INF);
            for (int j = i + 1; j <= n; j++) {
                bool f = true, _f = true;
                for (int k = 1; k <= p; k++) {
                    if(in[j][k] == 2);
                    else {
                        if(out[i][k] != in[j][k]) f = false;
                    }
                    if(in[i][k] == 2);
                    else {
                        if(out[j][k] != in[i][k]) _f = false;
                    }
                }
                if(f) add_edge(i+n, j, INF), vis[i][j] = true;
                if(_f) add_edge(j+n, i, INF), vis[j][i] = true;
            }
        }
        int ans = max_flow(0, 120);
        int cnt = 0;
        for (int i = 1; i <= n; i++) {
            if(g[i].size() == 0)continue;
            for (int j = 0; j < g[i].size(); j++) {
                if(g[i][j].to == 0 || g[i][j].to == 120 || g[i][j].to == i+n || g[i][j].to <= n ) continue;
                if(0 < g[i][j].w && vis[g[i][j].to-n][i]) {
                    res[++cnt].x = g[i][j].to - n;
                    res[cnt].y = i;
                    res[cnt].z = g[i][j].w;
                }
            }
        }
        printf("%d %d\n", ans, cnt);
        for (int i = 1; i <= cnt; i++) printf("%d %d %d\n", res[i].x, res[i].y, res[i].z);
    }
    return 0;
} 
View Code

最小费用最大流:

详见挑战程序设计

上下界网络流: 

https://www.cnblogs.com/NineSwords/p/9415962.html

posted @ 2018-04-26 17:26  Wisdom+.+  阅读(346)  评论(0编辑  收藏  举报