即一个带边权的网络流,给定一个流图以及每条边单位流的费用,求最大流情况下的最小费用。

这里因为带边权,所以之前的贴标签、分层算法均失去作用,只能用最普通的EK算法,即不断BFS找增广路,在此过程中,把费用看作边的长度(反向边为其相反数,保证增广过程可逆),一边找增广路,一边做spfa(带负权边只能用spfa)找最短路,这样找到的增广路一定是花费最少的。

代码如下:

#include<iostream>
#include<iomanip>
#include<ctime>
#include<climits>
#include<algorithm>
#include<queue>
#include<vector>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<map>
using namespace std;
typedef unsigned long long LL;
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define dep(i,a,b) for(int i=a;i>=b;i--)
inline int read(){
    int x=0;char ch=getchar();
    while(ch<'0'||ch>'9'){
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x;
}
const int MAXN=5001,MAXM=100001;
int head[MAXN],pre[MAXN],f[MAXN],e[MAXN],dis[MAXN],step[MAXN];
int flow[MAXM],to[MAXM],next[MAXM],cost[MAXM],cnt=0,n,m,s,t;
int Insert(int u,int v,int w,int c){//链式前向星加边
    to[cnt]=v;
    next[cnt]=head[u];
    cost[cnt]=c;
    flow[cnt]=w;
    head[u]=cnt++;
    to[cnt]=u;
    next[cnt]=head[v];
    cost[cnt]=-c;
    flow[cnt]=0;
    head[v]=cnt++;
}
int spfa(){
    memset(dis,127,sizeof(dis));//赋最大值
    dis[s]=0; 
    queue<int>q;
    memset(f,0,sizeof(f));
    q.push(s);f[s]=1;e[s]=INT_MAX;e[t]=0;
    while(!q.empty()){//SPFA
        int u=q.front();q.pop();f[u]=0;
        for(int i=head[u];i!=-1;i=next[i]){
            if(flow[i]&&dis[u]+cost[i]<dis[to[i]]){
                int p=to[i];
                e[p]=min(flow[i],e[u]);
                dis[p]=dis[u]+cost[i];
                pre[p]=u;
                step[p]=i;//记录连接节点的边的序号,在增广时方便查找
                if(!f[p]){
                    q.push(p);
                    f[p]=1;
                }
            }
        }
    }
    return e[t];
}
int COST=0,FLOW=0;
void MIN_COST(){
    int p;
    while(p=spfa()){
        int now=t;
        while(now!=s){
            flow[step[now]]-=p;
            flow[step[now]^1]+=p;
            now=pre[now];
        }
        COST+=dis[t]*p;//一次增广的花费为最短路径长度*增广流量
        FLOW+=p;
    }
}
int main(){
    n=read();m=read();s=read();t=read();
    memset(head,-1,sizeof(head));
    rep(i,1,m){
        int u=read(),v=read(),w=read(),c=read();
        Insert(u,v,w,c);
    }
    MIN_COST();
    printf("%d %d",FLOW,COST);
    return 0;
}