• 最小费用最大流(MCMF) 模板


    Description


      在图问题中当最大流有多组解时,给每条边在附上一个单位费用的量,问在满足最大流时的最小费用是多少?

      

    思路


      蒟蒻在这里套用一下博主STILLxjy的说法:

      给出一个容量网络,那他的最大流一定是一个定值(即使是有多个一样的最大值)。所以我们从开始的可行流开始增广时,最终的增广量是一定的。所以为了满足最小费用我们只需要每次找最小费用的增广路即可,直到流量为最大值。这个问题仅仅是在求增广路时先考虑费用最小的增广路,其他思想和EK思想一样。 
    我们学过SPFA求最短路算法(bellman-ford的队列优化),所以我们将弧的费用看做是路径长度,即可转化为求最短路的问题了。只需要所走的最短路满足两个条件即可:1可增广cap> flow,2路径变短d[v]>d[u]+cost< u,v> 。

      模板如下:

      刘汝佳的紫书模板,容易TLE,不适用于点多边少的情况:

    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    #include<vector>
    using namespace std;
    const int INF = 0x3f3f3f3f;
    const int maxn = 210;
    const int maxm = 220;
    
    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<int> G[maxn]; ////保存邻接关系
        int inq[maxn]; //是否在队列中,用于SPFA算法
        int d[maxn]; //源点到i的最短路径估计值
        int p[maxn]; //记录路径,记录上一条弧
        int a[maxn]; //找到增广路径后的改进量
    
        MCMF() {}
    
        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);
        }
    
        //队列优化的SPFA算法
        //寻找最小费用的增广路,使用引用同时修改原flow,cost
        bool bellmanFord(int s, int t, int& flow, long long& 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; //如果d[t]没有被更新,相当于没找到增广路径,则没有最大流也没有最小费用
            flow += a[t]; //更新最大流
            cost += (long long)d[t] * (long long)a[t]; ;//单位流量乘以单位路径长度用来计算消耗
            //通过使用p[]保存的上一个边的值来对刚刚找到的增广路径上面的流量进行更新
            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;
        }
    
        //需要保证初始网络中没有负权圈
        //计算从s到t的最小消耗cost,返回最大流
        int mincostMaxflow(int s, int t, long long& cost) {
            int flow = 0; cost = 0;
            while(bellmanFord(s, t, flow, cost));//不断寻找最短增广路径,直到找不到为止
            return flow;
        }
    }g;
    
    int main(void) {
        int nn, mm, s, t, u, v, cap, cost, totFlow = 0;
        long long totCost = 0;
        while(~scanf("%d%d", &nn, &mm)) {
            s = 1;
            t= nn;
            g.init(t+1);
            for (int i = 0; i < mm; i++) {
                scanf("%d%d%d%d", &u, &v, &cap, &cost);
                g.addEdge(u, v, cap, cost);
            }
            totFlow = g.mincostMaxflow(1, t, totCost);
            printf("最小花费:%lld 最大流:%d
    ", totCost, totFlow);
        }
        return 0;
    }
    View Code

      刘汝佳的白书模板,不用STL,很稳:

    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    #include<vector>
    using namespace std;
    const int INF = 0x3f3f3f3f;
    const int maxn = 210;
    const int maxm = 220;
    
    struct Edge {
        int from, to, cap, flow, cost;
        Edge () {} //作为Edge类数组的默认构造函数
        Edge (int u, int v, int ca, int fl, int co):from(u), to(v), cap(ca), flow(fl), cost(co)
        {}
    };
    
    struct MCFC {
        int n, m, s, t;
        Edge edges[maxm];
        int first[maxn];
        int next[maxm];
        int inq[maxn];
        int d[maxm];
        int p[maxn];
        int a[maxn];
        int Q[maxn];
    
        MCFC() {}
    
        void init(int n) {
            this->n = n;
            memset(first, -1, sizeof(first));
            m = 0;
        }
    
        void addEdge(int u, int v, int cap, int cost) {
            edges[m] = Edge(u, v, cap, 0, cost);
            next[m] = first[u];
            first[u] = m++;
            edges[m] = Edge(v, u, 0, 0, -cost);
            next[m] = first[v];
            first[v] = m++;
        }
    
        bool bellmanFord(int s, int t, int &flow, long long &cost) {
    
            for (int i = 0; i < n; i++) d[i] = INF;
            memset(inq, false, sizeof(inq));
            d[s] = 0; inq[s] = true; p[s] = 0; a[s] = INF;
            int front, rear;
            Q[rear = front = 0] = s;
            while (front <= rear) {
                int u = Q[front++];
                inq[u] = false;
                for (int i = first[u]; i != -1; i = next[i]) {
                    Edge &e = edges[i];
                    if (e.cap > e.flow && d[e.to] > d[u] + e.cost) {
                        d[e.to] = d[u] + e.cost;
                        p[e.to] = i;
                        a[e.to] = min(a[u], e.cap - e.flow);
                        if (!inq[e.to]) {Q[++rear] = e.to; inq[e.to] = true;}
                    }
                }
            }
            if (d[t] == INF) return false;
            flow += a[t];
            cost += (long long)d[t] * (long long)a[t];
            int u = t;
            while (u != s) {
                edges[p[u]].flow += a[t];
                edges[p[u]^1].flow -= a[t];
                u = edges[p[u]].from;
            }
            return true;
        }
    
        int mincostMaxflow(int s, int t, long long& cost) {
            int flow = 0; cost = 0;
            while (bellmanFord(s, t, flow, cost));
            return flow;
        }
    }g;
    
    int main(void) {
        int nn, mm, s, t, u, v, cap, cost, totFlow = 0;
        long long totCost = 0;
        while(~scanf("%d%d", &nn, &mm)) {
            s = 1;
            t= nn;
            g.init(t+1);
            for (int i = 0; i < mm; i++) {
                scanf("%d%d%d%d", &u, &v, &cap, &cost);
                g.addEdge(u, v, cap, cost);
            }
            totFlow = g.mincostMaxflow(1, t, totCost);
            printf("最小花费:%lld 最大流:%d
    ", totCost, totFlow);
        }
        return 0;
    }
    View Code

      

    ————全心全意投入,拒绝画地为牢
  • 相关阅读:
    pymongo中的连接操作:Connection()与MongoClient()
    Dynamics CRM2016 新功能之从CRM APP通过电子邮件发送页面链接
    [开发工具]_[Sublime Text 2]_[配置C++编译执行环境]
    struts2 全局拦截器,显示请求方法和參数
    A. Polo the Penguin and Strings
    linux驱动之LED驱动_1
    dbgrid控件如何能在左边显示行号?
    软件提示“没有活动事务”原因以及解决办法
    刷新dbgrid 而不失去当前行位置
    用ClientDataSet更新数据表,怎样自动生成行号? [问题点数:40分]
  • 原文地址:https://www.cnblogs.com/Bw98blogs/p/8818089.html
Copyright © 2020-2023  润新知