最近学了最小费用最大流,写一篇总结
前置知识:
- 最小费用的前提条件是最大流(本文提到的最大流算法为Dinic)
- 对于每条边来说,他们有着不一定相等的单位流量的价格,所以才有了"最小费用"这一条件
个人理解:
- 在普通的网络最大流中,常常使用类似while(bfs())while(flow=dinic(s,INF))maxflow+=flow;的方式求得结果
- 而在最小费用流中,原本的bfs()被替换成了spfa().而原本的bfs()是用来进行分层的,也就是保证始终有一条增广路.如果无法获取新的增广路了,也就可以输出结果了.在最小费用流中,我们在进行bfs的同时增加了对最小费用的剪枝,也就是spfa().
模板地址:https://www.luogu.org/problemnew/show/P3381
#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
const int MAXN=100100;
int head[MAXN],cnt=1;
int dis[MAXN],pre[MAXN],last[MAXN],flow[MAXN];
bool vis[MAXN];
struct Edge{
int to,w,f,nxt;
}e[MAXN];
queue<int>q;
void addEdge(int u,int v,int w,int f){//加边
e[++cnt].to=v,e[cnt].w=w,e[cnt].f=f,e[cnt].nxt=head[u],head[u]=cnt;
e[++cnt].to=u,e[cnt].w=0,e[cnt].f=-f,e[cnt].nxt=head[v],head[v]=cnt;
}
bool spfa(int s,int t){
memset(dis,0x7f,sizeof(dis));//清空数组
memset(flow,0x7f,sizeof(flow));
pre[t]=0;//将汇点的前继点设为空值
q.push(s);//初始化源点
dis[s]=0;//初始化dis
while(q.size()){//如果队列中还有结点就继续bfs
int u=q.front();q.pop();vis[u]=0;//提出点并设置该点并没有访问过
for(int i=head[u];i;i=e[i].nxt){
int y=e[i].to;
int newdis=dis[u]+e[i].f;//到点y的新代价
if(e[i].w&&dis[y]>newdis){//如果还有容量并且新路径代价较小
dis[y]=newdis;//设置到点y的代价为新代价
pre[y]=u;//到点y需要经过的点
last[y]=i;//到点y需要经过的边
flow[y]=min(flow[u],e[i].w);//能经过该点的最大流
if(!vis[y]){//如果该点没访问过
vis[y]=1;//将该点设为访问过
q.push(y);//向队列中加点
}
}
}
}
return pre[t];
}
int main(){
int n,m,s,t;
scanf("%d%d%d%d",&n,&m,&s,&t);
for(int i=1;i<=m;i++){//输入
int u,v,w,f;
scanf("%d%d%d%d",&u,&v,&w,&f);
addEdge(u,v,w,f);
}
int maxflow=0,mincost=0;
while(spfa(s,t)){//如果能继续增广
maxflow+=flow[t];//增加最大流
mincost+=flow[t]*dis[t];//增加最小费用
int now=t;//设置当前点为汇点
while(now!=s){
e[last[now]].w-=flow[t];
e[last[now]^1].w+=flow[t];
now=pre[now];
}
}
printf("%d %d\n",maxflow,mincost);//输出答案
return 0;
}