费用流
费用流
给出一个网络图,以及其源点和汇点,每条边已知其最大流量和单位流量费用,求出其网络最大流和在最大流情况下的最小费用。
注意,这里的费用意思是说每流一个单位的流量所消耗的费用。只需将Dinic中的bfs+dfs改为spfa即可。将费用看成距离,每次找到的最短路就是最小费用可行增广路。
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn=5005, maxm=1e5+5, INF=1e9;
int n, m, src, dst, maxf, minc;
inline int min(int x, int y){ return x<y?x:y; }
struct Edge{
int fr, to, nxt, f, v; //f:容量 v:费用
}e[maxm*2];
int cnte, fir[maxn];
void addedge(int x, int y, int flow, int val){
Edge &ed=e[++cnte];
ed.fr=x; ed.to=y; ed.nxt=fir[x]; ed.f=flow;
ed.v=val; fir[x]=cnte; }
int q[maxm], head, tail, dis[maxn], pre[maxn], flow[maxn], vis[maxn]; //pre表示上一条边
bool spfa(){ //每次找出费用最小的增广路
head=tail=0; q[tail++]=src; int u, v;
memset(vis, 0, sizeof(vis));
for (int i=1; i<=n; ++i) dis[i]=flow[i]=INF;
pre[dst]=0; dis[src]=0;
while (head<tail){
u=q[head++]; vis[u]=0;
for (int i=fir[u]; i; i=e[i].nxt){
v=e[i].to;
if (e[i].f&&dis[u]+e[i].v<dis[v]){
dis[v]=dis[u]+e[i].v; pre[v]=i;
flow[v]=min(flow[u], e[i].f);
if (!vis[v]) q[tail++]=v; vis[v]=1;
}
}
}
return pre[dst];
}
int main(){
scanf("%d%d%d%d", &n, &m, &src, &dst); int u, v, w, f;
for (int i=0; i<m; ++i){
scanf("%d%d%d%d", &u, &v, &w, &f);
addedge(u, v, w, f); addedge(v, u, 0, -f);
}
while (spfa()){
u=dst; maxf+=flow[dst];
minc+=flow[dst]*dis[dst]; //相当于费用和乘以流量
while (u!=src){
e[pre[u]].f-=flow[dst];
e[((pre[u]-1)^1)+1].f+=flow[dst];
u=e[pre[u]].fr;
}
}
printf("%d %d\n", maxf, minc);
return 0;
}