最大流

EK算法

复杂度O(n*m*m)

EK算法思路:1、利用bfs寻找增广路(因此也叫增广路算法)   2、若存在增广路,维护最大流

 

#include<iostream>
#include<queue>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
const int inf = 0x7fffffff;
const int maxn = (int)2e2 + 10;
int n, m;    //顶点数和边数
int G[maxn][maxn];  //存图G[i][j]=c 为点i到点j的边的容量为c
int pre[maxn];   //存储增广路,记录前驱,pre[i]=j为i的前驱为j
int flow[maxn];  //寻找增广路使记录流向每个节点的流量,flow[u]=k为起始点s流向u点的流量为k

int bfs(int s,int t){  //寻找一条从s到t的增广路,如果找到返回增广路的可行流,如果没找到返回-1用于标记
    queue<int> q;
    for (int i = 0; i <= n; i++) pre[i] = -1;
    pre[s] = 0; 
    flow[s] = inf;
    q.push(s);
    while (!q.empty()){
        int p = q.front(); q.pop();
        if (p == t) break;   //找到一条增广路
        for (int u = 1; u <= n; u++){
              //不是原点
            if (u!=s && pre[u] == -1 && G[p][u] > 0){ //该点没被走过并且该点的流量大于0
                pre[u] = p;
                flow[u] = min(flow[p],G[p][u]);
                q.push(u);
            }
        }
    }
    if (pre[t] == -1) return -1;
    return flow[t];
}
int EK(int s,int t){  //计算最大流,EK算法,复杂度O(n*m^2)
    int maxsum = 0, delta;    
    while ((delta=bfs(s,t))!=-1){
        //delta = bfs(s, t);
        //if (delta == -1) break;
        int p = t;    //从终点出发
        while (p != s){
            G[pre[p]][p] -= delta;    //正向边
            G[p][pre[p]] += delta;    //反向边
            p = pre[p];               //更新当前点
        }
        maxsum += delta;
    }
    return maxsum;
}
int main()
{
    int a, b, c;
    cin >> n >> m;
    memset(G, 0, sizeof(G));
    memset(flow, 0, sizeof(flow));
        
    for (int i = 0; i < m; i++){
        scanf("%d%d%d",&a,&b,&c);
            
        G[a][b] += c;
    }
    printf("%d\n",EK(1,n));
        
    return 0;
}
View Code

 

dinic算法

复杂度O(n*n*m)

dinic的实现是基于EK算法的,对EK算法进行优化

优化 1、利用bfs对图进行分层  2、dfs实现多次增广

 

#include<iostream>
#include<queue>
#include<algorithm>
#include<cstdio>
using namespace std;
const int maxn = 220;
const int inf = 0x7fffffff;
struct Edge{
    int to, w, next;
};
Edge e[maxn * 2];
int depth[maxn];     //顶点的层数
int head[maxn];      //实现链式前向星
int n, m, s, t;
int cnt;             //边的编号
void init(){
    cnt = 0;
    s = 1, t = n;
    for (int i = 0; i <= n; i++) 
        head[i] = -1;
}
void add_edge(int u, int v, int w){  //加边
    e[cnt].to = v; e[cnt].w = w;
    e[cnt].next = head[u]; head[u] = cnt++;
}
int bfs(int s,int t){    //分层
    queue<int> q;
    for (int i = 0; i <= n; i++) depth[i] = inf;
    q.push(s);
    depth[s] = 0;
    while (!q.empty()){
        int p = q.front(); q.pop();
        for (int i = head[p]; ~i; i = e[i].next){  //遍历以p为起始的边
            if (e[i].w>0 && depth[p]+1<depth[e[i].to]){ //如果是可行边,并且下一个点的深度和当前点的深度相差大于1
                depth[e[i].to] = depth[p] + 1;    //更新下一个点的深度
                q.push(e[i].to);
            }
        }
    }
    return depth[t] != inf;
}
int dfs(int u,int flow){    //多次增广 当前点u和flow为当前流量,一开始假设为inf
    if (u == t)     //如果找到汇点
        return flow;
    for (int i = head[u]; ~i; i = e[i].next){
        if (e[i].w > 0 && depth[u] + 1 == depth[e[i].to]){
            int delta = dfs(e[i].to, min(flow,e[i].w));  //向下增广
            if (delta > 0){       //增广成功
                e[i].w -= delta;
                e[i ^ 1].w += delta; 
                return delta;     //向上传递
            }
        }

    }
    return 0;  //没有增广路,返回0
}

int dinic(){   //dinic主体
    int res = 0;
    while (bfs(s,t)){
        while (int delta = dfs(s,inf))
            res += delta;
    }
    return res;
}
int main()
{
    ios::sync_with_stdio(false);
    cin >> n >> m;  //顶点数和边数
    init();
    int a, b, c;
    for (int i = 0; i < m; i++){
        cin >> a >> b >> c;
        add_edge(a, b, c);
        add_edge(b, a, 0);   //注意反向边初始为0
    }
    cout << dinic() << endl;
    return 0;
}
View Code

 

dinic算法+当前弧优化

当前弧优化是在dfs找增广路中增加cur[]数组记录下一次寻找的边,而不用每一次都从第一条边开始找

注意:每次分完层都要对cur[]数组初始化

 

#include<iostream>
#include<queue>
#include<algorithm>
#include<cstdio>
using namespace std;
const int maxn = 220;
const int inf = 0x7fffffff;
struct Edge{
    int to, w, next;
};
Edge e[maxn * 2];
int depth[maxn];     //顶点的层数
int head[maxn];      //实现链式前向星
int cur[maxn];       //当前弧优化,cur就是记录当前点u循环到了哪一条边
int n, m, s, t;
int cnt;             //边的编号
void init(){
    cnt = 0;
    s = 1, t = n;
    for (int i = 0; i <= n; i++) 
        head[i] = -1;
}
void add_edge(int u, int v, int w){  //加边
    e[cnt].to = v; e[cnt].w = w;
    e[cnt].next = head[u]; head[u] = cnt++;
}
int bfs(int s,int t){    //分层
    queue<int> q;
    for (int i = 0; i <= n; i++) depth[i] = inf;
    q.push(s);
    depth[s] = 0;
    while (!q.empty()){
        int p = q.front(); q.pop();
        for (int i = head[p]; ~i; i = e[i].next){  //遍历以p为起始的边
            if (e[i].w>0 && depth[p]+1<depth[e[i].to]){ //如果是可行边,并且下一个点的深度和当前点的深度相差大于1
                depth[e[i].to] = depth[p] + 1;    //更新下一个点的深度
                q.push(e[i].to);
            }
        }
    }
    return depth[t] != inf;
}
int dfs(int u,int flow){    //多次增广 当前点u和flow为当前流量,一开始假设为inf
    if (u == t)     //如果找到汇点
        return flow;
    for (int& i = cur[u]; ~i; i = e[i].next){    //----------修改部分
        if (e[i].w > 0 && depth[u] + 1 == depth[e[i].to]){
            int delta = dfs(e[i].to, min(flow,e[i].w));  //向下增广
            if (delta > 0){       //增广成功
                e[i].w -= delta;
                e[i ^ 1].w += delta; 
                return delta;     //向上传递
            }
        }

    }
    return 0;  //没有增广路,返回0
}

int dinic(){   //dinic主体
    int res = 0;
    while (bfs(s,t)){
        
        for (int i = 0; i <= n; i++) cur[i] = head[i];  //-------修改部分,注意每次初始化cur

        while (int delta = dfs(s,inf))
            res += delta;
    }
    return res;
}
int main()
{
    ios::sync_with_stdio(false);
    cin >> n >> m;  //顶点数和边数
    init();
    int a, b, c;
    for (int i = 0; i < m; i++){
        cin >> a >> b >> c;
        add_edge(a, b, c);
        add_edge(b, a, 0);   //注意反向边初始为0
    }
    cout << dinic() << endl;
    
    return 0;
}
View Code

 

posted @ 2019-03-01 13:40  looeyWei  阅读(287)  评论(0)    收藏  举报