<题目链接>
题目大意:
给定一张图,给定条边的容量和单位流量费用,并且给定源点和汇点。问你从源点到汇点的最带流和在流量最大的情况下的最小费用。
解题分析:
最小费用最大流果题。
下面的是MCMF的模板。想学ZKW费用流和最小费用流的原始对偶 (Primal-Dual) 算法的同学,可以看看ZKW本人(Orz)的讲解 >>>
#include <bits/stdc++.h> using namespace std; int h[5210],d[5210],used[5210],que[100010],last[5210]; int cnt=1,INF=0x3f3f3f3f,ans1=0,ans2=0; #define clr(a,b) memset(a,b,sizeof(a)) template<typename T> inline void read(T&x){ x=0;int f=1;char c=getchar(); while(c<'0' || c>'9'){ if(c=='-')f=-1;c=getchar(); } while(c>='0' && c<='9'){ x=x*10+c-'0';c=getchar(); } x*=f; } struct Edge{ int to,cap,cost,next; }e[120010]; inline void add(int from,int to,int c1,int c2){ e[++cnt]=(Edge){to,c1,c2,h[from]};h[from]=cnt; e[++cnt]=(Edge){from,0,-c2,h[to]};h[to]=cnt; } bool spfa(int s,int t){ //slf优化 clr(last,0);clr(d,INF);clr(used,0); int head,tail; tail=head=50002; que[tail]=s;used[s]=1;d[s]=0; while(head<=tail){ //数组模拟双端队列 int x=que[head++]; for(int i=h[x];i;i=e[i].next){ if(e[i].cap&&d[x]+e[i].cost<d[e[i].to]){ //如果这条路上还有残余容量,就更新其费用,让其费用最小 d[e[i].to]=d[x]+e[i].cost; last[e[i].to]=i; //记录这个点的最大费用所对应的前一条边的编号 if(!used[e[i].to]){ if(d[e[i].to]<d[que[head]])que[--head]=e[i].to; //如果这个点的费用小于队列的头部的话,就将它塞入队列头部 else que[++tail]=e[i].to; //否则的话,塞入队尾 used[e[i].to]=1; } } } used[x]=0; } return d[t]!=INF; } void MCMF(int t){ int minn=INF; for(int i=last[t];i;i=last[e[i^1].to])minn=min(minn,e[i].cap); //沿着那个记录的反向增广路径更新这条路上的最小容量 ans1+=minn; for(int i=last[t];i;i=last[e[i^1].to]){ ans2+=e[i].cost*minn; //费用=单位流量费用*流量 e[i].cap-=minn; //正向边容量-=minn e[i^1].cap+=minn; //反向边容量+=minn } } int main(){ int n,m,s,t; read(n);read(m);read(s);read(t); for(int i=1;i<=m;i++){ int x,y,w,f;read(x);read(y);read(w);read(f); add(x,y,w,f); } while(spfa(s,t))MCMF(t); printf("%d %d ",ans1,ans2); }