费用流

费用流

给出一个网络图,以及其源点和汇点,每条边已知其最大流量和单位流量费用,求出其网络最大流和在最大流情况下的最小费用。

注意,这里的费用意思是说每流一个单位的流量所消耗的费用。只需将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;
}

猜你喜欢

转载自www.cnblogs.com/MyNameIsPc/p/9141995.html