本文非原创,大部分参考自秋日私语大大的题解。
GO:模板
最大流问题,我们可以通过EK,DINIC等方法解决,但是如果在每条边上加上边权,并定义 费用 为边权乘以流量,在所有最大流中求出费用最少的那一个方案,就需要新的算法了。
这里只对最小费用最大流的EK-SPFA算法做一些自己的解释,并会附上模板。
首先是EK算法:
1.我们每次求一条源点到汇点的路径,流量最大为路径上的最小流量minflow,所以下一次寻找最大流就不能走那条,于是将所有边的流量减小minflow。
2.由1能保证求出最大流吗?答案是否定的,因为当我们遇到一些特殊的图时:

比如这个,第一条路径S-2-T,边上流量变为0,得到maxflow=4,实际上maxflow=6,因此我们要加一条反向边让路径有“反悔”的机会。
3.我们使用bfs进行增广,这样能保证在更少的次数下求出最大流。
4.对2具体的操作:记录路径上每个点的前驱和前驱边,走完spfa后从后向前遍历前驱边,修改流量。
最小费用最大流:
众所周知,SPFA他死了。
但这并不影响我们做网络流的题目(出题人没必要在网络流的题目上用图论卡我们),我们借鉴spfa的思想,对路径上求最短路。
具体看代码。
1 //EK-MCMF
2 #include<bits/stdc++.h>
3 using namespace std;
4 const int inf=1e9;
5 #define f(i,a,b) for(register int i=a;i<=b;i++)
6 const int N=5e4+10;
7 int head[N<<1],flow[N],l[N],dis[N],pre[N],vis[N],cnt=-1,n,m,s,t,ansf,ansc;
8 //flow判断路径最小流量,dis判断最短路,pre是前驱点,l是前驱边,vis判断是否在队列中
9 queue<int> q;
10 struct edge{int to,next,f,w;}e[N<<1];
11 void addedge(int from,int to,int f,int w){e[++cnt]=(edge){to,head[from],f,w}; head[from]=cnt;}
12 bool spfa(){
13 memset(dis,0x7f,sizeof(dis));
14 memset(vis,0,sizeof(vis));
15 vis[s]=1;
16 dis[s]=pre[t]=0;
17 flow[s]=inf;
18 q.push(s);
19 while(!q.empty()){
20 int u=q.front();
21 vis[u]=0;
22 for(int i=head[u];i!=-1;i=e[i].next){
23 int v=e[i].to,w=e[i].w,f=e[i].f;
24 if(f>0&&dis[v]>dis[u]+w){
25 dis[v]=dis[u]+w;
26 pre[v]=u;
27 l[v]=i;
28 flow[v]=min(flow[u],f);
29 if(!vis[v]){
30 q.push(v);
31 vis[v]=1;
32 }
33 }
34 }
35 q.pop();
36 }
37 return pre[t];
38 }
39 void solve(){
40 while(spfa()){
41 ansf+=flow[t];
42 ansc+=dis[t]*flow[t];
43 for(int i=t;i!=s;i=pre[i]){
44 int edge_=l[i];
45 e[edge_].f-=flow[t];
46 e[edge_^1].f+=flow[t];
47 }
48 }
49 printf("%d %d",ansf,ansc);
50 }
51 int main(){
52 memset(head,-1,sizeof(head));
53 scanf("%d%d%d%d",&n,&m,&s,&t);
54 int x,y,f,w;
55 f(i,1,m){
56 scanf("%d%d%d%d",&x,&y,&f,&w);
57 addedge(x,y,f,w);
58 addedge(y,x,0,-w);
59 }
60 solve();
61 return 0;
62 }