填坑。
前两期:
网络流初步 —— 最大流算法 (2023/01/25)
网络流初步(2)—— 最小割 (2023/01/26)
费用流
现在,网络中新增了一个属性:费用。
设边 u,v 的费用为 w(u,v)。则当 (u,v) 的流量为 f(u,v) 时,费用为 f(u,v)w(u,v)。
现在,你需要保证流量最大的前提下,使费用最小,称作最小费用最大流(Min Cost Max Flow)。
做法
算法
用贪心算法。只需每次找费用最小的增广路,然后不断增广即可,证明略去,但是记住只要没有负圈这个算法就是正确的。(注意:原图费用不可以出现负圈)
数据结构
怎么存图?注意在费用流中是需要处理重边问题的,还需要方便的获取反向边。
习惯上,我们采用的是边表+邻接表记录出边在边表中的下标的数据结构。一条边和他反向边是存在一起的,即在边表的下标可以通过异或 1 得到。(0^1=1,1^1=0,6^1=7,7^1=6)
如果不明白,看以下代码理解。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| struct Edge{ int u,v; ll w,c,f; };
vector<Edge> edges; vector<int> G[N];
void add_edge(int u,int v,int w,int c){ edges.push_back({u,v,w,c,0}); edges.push_back({v,u,-w,0,0}); auto cnt = edges.size(); G[u].push_back(cnt-2); G[v].push_back(cnt-1); }
|
找费用最小的增广路
用 SPFA,即队列优化的 Bellman-Ford 算法。找到后和 EK 算法思路一样进行增流。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| const ll INF = 0x3f3f3f3f3f3f3f3f; int s,t; ll d[N]; bool inq[N]; int fa[N]; ll a[N];
ll flow=0,cost=0;
bool SPFA(){ memset(d,0x3f,sizeof(d)); d[s]=0;a[s]=INF; queue<int> q; q.push(s);inq[s]=1; while(q.size()){ int u=q.front(); q.pop();inq[u]=0; for(int ind:G[u]){ auto& edge = edges[ind]; if(edge.c > edge.f && d[edge.v] > d[u] + edge.w){ d[edge.v]=d[u]+edge.w; a[edge.v]=min(a[u],edge.c-edge.f); fa[edge.v] = ind; if(!inq[edge.v]){ q.push(edge.v);inq[edge.v]=1; } } } }
if(d[t]==INF)return 0;
flow += a[t]; cost += a[t]*d[t]; int now = t; while(now!=s){ int ind = fa[now]; edges[ind].f += a[t]; edges[ind^1].f -= a[t]; now = edges[ind].u; } return 1; }
|
主过程
只需要一行即可。
完整代码
洛谷P3381 【模板】最小费用最大流
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
| #include<bits/stdc++.h> using namespace std;
using ll=long long;
const int N = 5e3+5;
struct Edge{ int u,v; ll w,c,f; };
vector<Edge> edges; vector<int> G[N];
void add_edge(int u,int v,int w,int c){ edges.push_back({u,v,w,c,0}); edges.push_back({v,u,-w,0,0}); auto cnt = edges.size(); G[u].push_back(cnt-2); G[v].push_back(cnt-1); }
const ll INF = 0x3f3f3f3f3f3f3f3f; int s,t; ll d[N]; bool inq[N]; int fa[N]; ll a[N];
ll flow=0,cost=0;
bool SPFA(){ memset(d,0x3f,sizeof(d)); d[s]=0;a[s]=INF; queue<int> q; q.push(s);inq[s]=1; while(q.size()){ int u=q.front(); q.pop();inq[u]=0; for(int ind:G[u]){ auto& edge = edges[ind]; if(edge.c > edge.f && d[edge.v] > d[u] + edge.w){ d[edge.v]=d[u]+edge.w; a[edge.v]=min(a[u],edge.c-edge.f); fa[edge.v] = ind; if(!inq[edge.v]){ q.push(edge.v);inq[edge.v]=1; } } } }
if(d[t]==INF)return 0;
flow += a[t]; cost += a[t]*d[t]; int now = t; while(now!=s){ int ind = fa[now]; edges[ind].f += a[t]; edges[ind^1].f -= a[t]; now = edges[ind].u; } return 1; }
int main(){ int n,m; scanf("%d%d%d%d",&n,&m,&s,&t); for(int i=0;i<m;i++){ int u,v,w,c; scanf("%d%d%d%d",&u,&v,&w,&c); add_edge(u,v,c,w); }
while(SPFA());
printf("%lld %lld\n",flow,cost); return 0; }
|