zoukankan      html  css  js  c++  java
  • 最小费用最大流模板

    费用流

    假设每条边除了有一个容量限制外,还有一个单位流量所需的费用(cost)。该网络中花费最小的最大流称为最小费用最大流,即总流量最大的情况下,总费用最小的流。

    和 Edmonds-Karp 算法类似,但每次用 Bellman-Ford 算法而非 BFS 找增广路。只要初始流是该流量下的最小费用可行流,每次增广后的新流都是新流量下的最小费用流。

    Bellman-Ford版

    如何用优先队列代替Bellman-Ford中的普通队列就是SPFA版。

    时间复杂度 $O(FEV)$

    struct Edge{
        int from, to, cap, flow, cost;
        Edge(int u, int v, int c, int f, int w):from(u), to(v), cap(c), flow(f), cost(w){}
    };
    
    struct MCMF
    {
        int n, m;
        vector<Edge>edges;
        vector<ll>G[maxn];
        int inq[maxn];      //是否在队列中
        int d[maxn];        //Bellman-Ford
        int p[maxn];        //上一条弧
        int a[maxn];         //可改进量
    
        void init(int n)
        {
            this->n = n;
            for(int i = 0;i < n;i++)  G[i].clear();
            edges.clear();
        }
    
        void AddEdge(int from, int to, int cap, int cost)
        {
            edges.push_back(Edge(from, to, cap, 0, cost));      //有向边
            edges.push_back(Edge(to ,from, 0, 0, -cost));
            m = edges.size();
            G[from].push_back(m-2);
            G[to].push_back(m-1);
        }
    
        bool BellmanFord(int s, int t, int& flow, int& cost)
        {
            for(int i = 0;i < n;i++)  d[i] = INF;
            memset(inq, 0, sizeof(inq));
            d[s] = 0; inq[s] =1; p[s] = 0;a[s] = INF;
    
            queue<int>Q;;
            Q.push(s);
            while(!Q.empty())
            {
                int u = Q.front(); Q.pop();
                inq[u] = 0;
                for(int i = 0;i < G[u].size();i++)
                {
                    Edge& e = edges[G[u][i]];
                    if(e.cap > e.flow && d[e.to] > d[u] + e.cost)
                    {
                        d[e.to] = d[u] + e.cost;
                        p[e.to] = G[u][i];
                        a[e.to] = min(a[u], e.cap - e.flow);
                        if(!inq[e.to]){ Q.push(e.to); inq[e.to] = 1;}
                    }
                }
            }
            if(d[t] == INF)  return false;
            flow += a[t];
            cost += d[t] * a[t];
            for(int u = t;u != s;u = edges[p[u]].from)
            {
                edges[p[u]].flow += a[t];
                edges[p[u]^1].flow -=a[t];
            }
            return true;
        }
    
        //需要保证初始网络中没有负权圈
        int MaxcostMaxflow(int s, int t, int& cost)
        {
            int flow = 0; cost = 0;
            while(BellmanFord(s, t, flow, cost));
            return flow;
        }
    }mcmf;

    Dijkstra版

    可以采用加入“势函数”后的Dijkstra算法。因为对于每一条边 $e=(u, v)$,有如下事实成立:$h(v) leq h(u)+e.cost$ (其中 $h(u)$ 表示 $s$ 到 $u$ 的最短距离),因此令 $dis[v] = dis[u] + e.cost + h[u] - h[v]$,那么所有的 $dist$ 值必然大于等于0,这样就能用 $Dijkstra$ 求解了。

    下面代码中用了一个优先队列,每次优先队列列出 $dist$ 值小的元素。

    整个算法的时间复杂度为 $O(FElogV)$($F$ 是流量,$E$ 是边数,$V$ 是顶点数)。

    struct edge {
        int to, capacity, cost, rev;
        edge() {}
        edge(int to, int _capacity, int _cost, int _rev) :to(to), capacity(_capacity), cost(_cost), rev(_rev) {}
    };
    struct Min_Cost_Max_Flow {
        int V, H[maxn + 5], dis[maxn + 5], PreV[maxn + 5], PreE[maxn + 5];
        vector<edge> G[maxn + 5];
        //调用前初始化
        void Init(int n) {
            V = n;
            for (int i = 0; i <= V; ++i)G[i].clear();
        }
        //加边
        void Add_Edge(int from, int to, int cap, int cost) {
            G[from].push_back(edge(to, cap, cost, G[to].size()));
            G[to].push_back(edge(from, 0, -cost, G[from].size() - 1));
        }
        //flow是自己传进去的变量,就是最后的最大流,返回的是最小费用
        int Min_cost_max_flow(int s, int t, int f, int& flow) {
            int res = 0; fill(H, H + 1 + V, 0);
            while (f) {
                priority_queue <pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>> > q;
                fill(dis, dis + 1 + V, INF);
                dis[s] = 0; q.push(pair<int, int>(0, s));
                while (!q.empty()) {
                    pair<int, int> now = q.top(); q.pop();
                    int v = now.second;
                    if (dis[v] < now.first)continue;
                    for (int i = 0; i < G[v].size(); ++i) {
                        edge& e = G[v][i];
                        if (e.capacity > 0 && dis[e.to] > dis[v] + e.cost + H[v] - H[e.to]) {
                            dis[e.to] = dis[v] + e.cost + H[v] - H[e.to];
                            PreV[e.to] = v;
                            PreE[e.to] = i;
                            q.push(pair<int, int>(dis[e.to], e.to));
                        }
                    }
                }
                if (dis[t] == INF)break;
                for (int i = 0; i <= V; ++i)H[i] += dis[i];
                int d = f;
                for (int v = t; v != s; v = PreV[v])d = min(d, G[PreV[v]][PreE[v]].capacity);
                f -= d; flow += d; res += d*H[t];
                for (int v = t; v != s; v = PreV[v]) {
                    edge& e = G[PreV[v]][PreE[v]];
                    e.capacity -= d;
                    G[v][e.rev].capacity += d;
                }
            }
            return res;
        }
        int Max_cost_max_flow(int s, int t, int f, int& flow) {
            int res = 0;
            fill(H, H + 1 + V, 0);
            while (f) {
                priority_queue <pair<int, int>> q;
                fill(dis, dis + 1 + V, -INF);
                dis[s] = 0;
                q.push(pair<int, int>(0, s));
                while (!q.empty()) {
                    pair<int, int> now = q.top(); q.pop();
                    int v = now.second;
                    if (dis[v] > now.first)continue;
                    for (int i = 0; i < G[v].size(); ++i) {
                        edge& e = G[v][i];
                        if (e.capacity > 0 && dis[e.to] < dis[v] + e.cost + H[v] - H[e.to]) {
                            dis[e.to] = dis[v] + e.cost + H[v] - H[e.to];
                            PreV[e.to] = v;
                            PreE[e.to] = i;
                            q.push(pair<int, int>(dis[e.to], e.to));
                        }
                    }
                }
                if (dis[t] == -INF)break;
                for (int i = 0; i <= V; ++i)H[i] += dis[i];
                int d = f;
                for (int v = t; v != s; v = PreV[v])d = min(d, G[PreV[v]][PreE[v]].capacity);
                f -= d; flow += d;
                res += d*H[t];
                for (int v = t; v != s; v = PreV[v]) {
                    edge& e = G[PreV[v]][PreE[v]];
                    e.capacity -= d;
                    G[v][e.rev].capacity += d;
                }
            }
            return res;
        }
    }mcmf;

    注:

    • 如果是求最大费用,只需加边是边权取反,最终费用也取反
    • 第一种可能被卡时间

    参考链接:https://blog.csdn.net/u014800748/article/details/44059993

  • 相关阅读:
    (萌O(∩_∩)O)哈希知识点小结
    hash应用以及vector的使用简介:POJ 3349 Snowflake Snow Snowflakes
    BestCoder Round #3HDU 4907
    搜索:POJ2251&POJ1426&POJ3087&POJ2488
    母函数初学四大题
    欧拉函数知识点总结及代码模板及欧拉函数表
    欧几里德与扩展欧几里德算法以及青蛙的约会~
    KMP 知识点总结
    HDU 1142 A Walk Through the Forest(dijkstra+记忆化DFS)
    HDU 1535 Invitation Cards(SPFA,及其优化)
  • 原文地址:https://www.cnblogs.com/lfri/p/11267000.html
Copyright © 2011-2022 走看看