最大流——EK&&Dinic

学习地方:http://www.wutianqi.com/?p=3107

 

EK模板:O(V*E^2)

临街矩阵实现:

int bfs(int s,int e){
    int i;
    queue<int>q;
    CL(vt,false); CL(pre,-1);//初始化
    for (i = 0; i <= n + 1; ++i) f[i] = inf;//注意这里的范围
    q.push(s);
    vt[s] = true;
    while (!q.empty()){
        int u = q.front(); q.pop();
        for (i = 0; i <= n + 1; ++i){
            //如果回到父亲节点,或者该点已经被访问过,或者这条路不满足有流量
            if (i == u || vt[i] || pre[i] != -1 || mp[u][i] <= 0) continue;
            vt[i] = true;
            f[i] = min(f[u],mp[u][i]);//选出该路径上的最小流量
            pre[i] = u;//记录i的父亲为u
            q.push(i);
            if (i == e){
                flag = true;
                break;
            }
        }
        if (flag) break;
    }
    if (f[e] == inf) return -1;
    else return f[e];
}
int EK(int s,int e){
    int i;
    int ans = 0;
    while (1){
        flag = false;
        int d = bfs(s,e);//寻找可行流
        if (d == -1) break;
        //更新流量
        for (i = e; i != s; i = pre[i]){
            int u = pre[i];
            mp[u][i] -= d;
            mp[i][u] += d;
        }
        ans += d;
    }
    return ans;
}

  

  

邻接表实现:

struct node{
    int v,w;
    int next;
}g[M*4 + 10];

int head[N],ct;

void init(){
    ct = 0;
    CL(head,-1);
}
void add(int u,int v,int w){
    g[ct].v = v;
    g[ct].w = w;
    g[ct].next = head[u];
    head[u] = ct++;

    g[ct].v = u;
    g[ct].w = 0;
    g[ct].next = head[v];
    head[v] = ct++;
}
int bfs(int s,int e){
    int i;

    for (i = 0; i <= n; ++i) f[i] = inf;
    CL(vt,false); CL(pre,-1); CL(path,-1);
    queue<int>q;
    q.push(s); vt[s] = true;

    while (!q.empty()){

        int u = q.front(); q.pop();
        for (i = head[u]; i != -1; i = g[i].next){
            int v = g[i].v;
            if (vt[v] ||  g[i].w <= 0 || pre[g[i].v] != -1) continue;
            pre[v] = u; path[v] = i; vt[v] = true;//多了一个path记录可行流这条路上的路的编号
            f[v] = min(f[u],g[i].w);
            if (v == e) break;
            q.push(v);
        }
    }
    if (f[e] == inf) return -1;
    else return f[e];
}
int EK(int s,int e){
    int i,ans = 0;
    while (1){
        int d = bfs(s,e);
        if (d == -1) break;
        int tp;
        for (i = e; i != s; i = pre[i]){
            tp = path[i];//去路径编号
            g[tp].w -= d;
            g[tp^1].w += d;
        }
        ans += d;
    }
    return ans;
}

还有一种写法就是记录逆向边,这里不用记录们因为当前变与逆变abs(差值)= 1,取抑或即可。另一种写法http://blog.sina.com.cn/s/blog_6635898a0100ly53.html
 

  

Dinic模板:

 邻接表实现:

struct node{
    int v,w;
    int next;
}g[M*4 + 10];
int head[N],ct;

void init(){
    ct = 0;
    CL(head,-1);
}
void add(int u,int v,int w){
    g[ct].v = v;
    g[ct].w = w;
    g[ct].next = head[u];
    head[u] = ct++;

    g[ct].v = u;
    g[ct].w = 0;
    g[ct].next = head[v];
    head[v] = ct++;
}
//分层
bool layer(int s,int e){
    int i;
    CL(level,-1);
    level[s] = 0;
    int l,r;
    l = r = 0;
    q[r] = s;
    while (l <= r){
        int u = q[l++];
        for (i = head[u]; i != -1; i = g[i].next){
            int v = g[i].v;
            if (level[v] == -1 && g[i].w > 0){
                level[v] = level[u] + 1;
                q[++r] = v;
                if (v == e) return true;
            }
        }
    }
    return false;
}
int find(int s,int e){
    int i;
    int ans = 0;
    int top = 1;

    while (top){
        int u = (top == 1 ? s : g[q[top - 1]].v);//如果没有变肯定是起点,否则就是上一个边终点

        if (u == e){
            int MIN = inf,pos;
            //找出最小流量
            for (i = 1; i < top; ++i){
                int tp = q[i];//注意这里取边的编号
                if (g[tp].w < MIN){
                    MIN = g[tp].w;
                    pos = i;
                }
            }
            //更新容量
            for (i = 1; i < top; ++i){
                int tp = q[i];//注意这里取边的编号
                g[tp].w -= MIN;
                g[tp^1].w += MIN;
            }
            ans += MIN;
            top = pos;
        }
        else{//找可行流
            for (i = head[u]; i != -1; i = g[i].next){
                int v = g[i].v;
                if (g[i].w > 0 && level[v] == level[u] + 1){
                    q[top++] = i;
                    break;
                 }
            }
            if (i == -1){//如果u没有可走的子节点
                top--;
                level[u] = -1;
            }
        }
    }
    return ans;
}
int Dinic(int s,int e){
    int ans = 0;
    while (layer(s,e)) ans += find(s,e);//分层成功找可行流
    return ans;
}

  

 上边的Dinic在写最大权闭合图时,超时,后来又搜了一个新模板:

bool layer(int s,int e)
{
    int i;
    CL(level,-1);
    level[s] = 0;
    int l = 0, r= 0;
    q[r] = s;
    while (l <= r){
        int u = q[l++];
        for (i = head[u]; i != -1; i = g[i].next)
        {
            int v = g[i].v;
            if (level[v] == -1 && g[i].w > 0)
            {
                level[v] = level[u] + 1;
                q[++r] = v;
                if (v == e) return true;
            }
        }
    }
    return false;
}

ll dinic(int s,int e){
    ll ans = 0;
    while (layer(s,e))
    {
        int top = 0,u = s,i;
        for (i = s; i <= e; ++i) out[i] = head[i];
        while (out[s] != -1)
        {
            if (u == e)
            {
                ll MIN = inf;
                for (i = top - 1; i >= 0; --i)
                {
                    MIN = min(MIN,g[q[i]].w);
                }
                for (i = top - 1; i >= 0; --i)
                {
                    g[q[i]].w -= MIN;
                    g[q[i]^1].w += MIN;
                    if (g[q[i]].w == 0) top = i;
                }
                ans += MIN;
                u = g[q[top]].u;
            }
            else if (out[u] != -1 && g[out[u]].w > 0 && level[u] + 1 == level[g[out[u]].v])
            {
                q[top++] = out[u];
                u = g[out[u]].v;
            }
            else
            {
                while (top > 0 && u != s && out[u] == -1)
                u = g[q[--top]].u;
                out[u] = g[out[u]].next;
            }
        }
    }
    return ans;
}

  

 

 

posted @ 2012-03-16 21:18  E_star  阅读(335)  评论(0编辑  收藏  举报