最小费用最大流肯定要保证最大流,所以它和最大流有一些类似的性质。如果把费用看成边,就可以每次走最短路(保证费用最小),走到不能走为止(保证最大流)。费用流版的ek就是这样。需要注意的是,反向弧的边权为它对应的正向弧的费用的相反数,所以最短路要用spfa来求。
费用流版的dinic,又叫zkw费用流,还是多路增广的思想。
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cstdlib> #include<cmath> #include<iomanip> #include<queue> #define maxn 5010 #define maxm 100010 using namespace std; int fir[maxn],nxt[maxm],v[maxm],fl[maxm],w[maxm],cnt; int n,m,dis[maxn],s,t,inf[5],mincost,maxflow; int p[maxn],flp[maxn],kp[maxn]; bool vis[maxn]; int read() { int x = 0, f = 1; char c = getchar(); while (!isdigit(c)) { if (c == '-') f = -1; c = getchar(); } while(isdigit(c)) x = x*10 + c - '0', c = getchar(); return x*f; } void addedge(int u1,int v1,int fl1,int w1) { v[cnt]=v1,w[cnt]=w1,fl[cnt]=fl1,nxt[cnt]=fir[u1],fir[u1]=cnt++; v[cnt]=u1,w[cnt]=-w1,fl[cnt]=0,nxt[cnt]=fir[v1],fir[v1]=cnt++; } bool spfa() { memset(dis,0x7f,sizeof(dis)); memset(vis,0,sizeof(vis)); queue<int>q; dis[s]=0; vis[s]=1; flp[s]=inf[0]; q.push(s); while(!q.empty()) { int u=q.front();q.pop(); for(int k=fir[u];k!=-1;k=nxt[k]) { int vv=v[k]; if(fl[k]>0) { if(dis[vv]>dis[u]+w[k]) { dis[vv]=dis[u]+w[k]; if(!vis[vv])q.push(vv); p[vv]=u; kp[vv]=k; flp[vv]=min(flp[u],fl[k]); vis[vv]=1; } } } vis[u]=0; } return dis[t]==inf[0]?0:1; } void dfs() { for(int i=t;i!=s;i=p[i]) { mincost+=w[kp[i]]*flp[t]; fl[kp[i]]-=flp[t]; fl[kp[i]^1]+=flp[t]; } maxflow+=flp[t]; return; } int main() { memset(fir,-1,sizeof(fir)); memset(inf,0x7f,sizeof(inf)); n=read(),m=read(),s=read(),t=read(); for(int i=1;i<=m;i++) { int x=read(),y=read(),z=read(),k=read(); addedge(x,y,z,k); } while(spfa())dfs(); cout<<maxflow<<" "<<mincost; }