最小费用最大流(MCMF) 模板
Description
在图问题中当最大流有多组解时,给每条边在附上一个单位费用的量,问在满足最大流时的最小费用是多少?
思路
蒟蒻在这里套用一下博主STILLxjy的说法:
给出一个容量网络,那他的最大流一定是一个定值(即使是有多个一样的最大值)。所以我们从开始的可行流开始增广时,最终的增广量是一定的。所以为了满足最小费用我们只需要每次找最小费用的增广路即可,直到流量为最大值。这个问题仅仅是在求增广路时先考虑费用最小的增广路,其他思想和EK思想一样。
我们学过SPFA求最短路算法(bellman-ford的队列优化),所以我们将弧的费用看做是路径长度,即可转化为求最短路的问题了。只需要所走的最短路满足两个条件即可:1可增广cap> flow,2路径变短d[v]>d[u]+cost< u,v> 。
模板如下:
刘汝佳的紫书模板,容易TLE,不适用于点多边少的情况:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#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\n", totCost, totFlow); } return 0; }
刘汝佳的白书模板,不用STL,很稳:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#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\n", totCost, totFlow); } return 0; }
————全心全意投入,拒绝画地为牢