avatar
fireworks99
keep hungry keep foolish

最小费用流(沿Dijkstra最短路增广)

最小费用流

最小费用流问题:

在最大流问题的网络中,给边新加上费用,求流量为f时费用的最小值

最大流:残余网络上贪心增广

最小费用流:残余网络上沿着最短路增广

Attention

残余网络中反向边的权值是原边权值的相反数,意味着会有负权边,所以一般用Bellman_Ford求最短路。但它时间复杂度较高,可以考虑导入“”的概念用Dijkstra求最短路

Dijkstra最短路上增广(模板)

#include <queue>
#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;
const int N = 1005;
const int INF = 0x3f3f3f3f;
typedef pair<int, int> P;///first:最短距离 second:节点编号
typedef long long ll;

struct edge{ int to, w, cap, rev; };

int n, m, s, t, flow;
vector<edge> vec[N];
int h[N], dis[N];///节点的势 最短距离
int fro_v[N], fro_e[N];///vector 的 第一维和第二维
///用于确定最短路中点k前面那条边

void add(int from, int to, int w, int cap)
{
    int from_sz = vec[from].size(), to_sz = vec[to].size();
    vec[from].push_back( (edge){ to, w, cap, to_sz } );
    vec[to].push_back( (edge){ from, -w, 0, from_sz } );
}

int min_cost_flow(int s, int t, int f)
{
    ll res = 0;
    memset(h, 0, sizeof(h));
    while(f)///若没完成任务:就对残余网络搜索最短路进行增广
    {
        priority_queue<P, vector<P>, greater<P> > q;
        memset(dis, INF, sizeof(dis));
        dis[s] = 0;
        q.push(P(0, s));
        while(!q.empty())
        {
            P now = q.top();
            q.pop();
            int to = now.second;
            if(dis[to] < now.first)
                continue;
            for(int i = 0; i < vec[to].size(); ++i)
            {
                edge & nxt = vec[to][i];
                if(nxt.cap > 0 && dis[nxt.to] > dis[to] + nxt.w + h[to] - h[nxt.to])
                {
                    dis[nxt.to] = dis[to] + nxt.w + h[to] - h[nxt.to];
                    fro_v[nxt.to] = to;
                    fro_e[nxt.to] = i;
                    q.push(P(dis[nxt.to], nxt.to));
                }
            }
        }
        if(dis[t] == INF)///还未完成任务(f减为0)就找不出可行流了
            return -1;
        for(int v = 0; v <= n; ++v)
            h[v] += dis[v];

        int d = f;///保证f最终为0而非小于0
        ///即使新的最大流>f, 也只取f
        for(int v = t; v != s; v = fro_v[v])
            d = min(d, vec[ fro_v[v] ][ fro_e[v] ].cap);

        f -= d;
        res += d * h[t];///There is a h[t] in Dijkstra, not a dis[t](in Bellman_Ford).
        for(int v = t; v != s; v = fro_v[v])///更新残余网络,建反向边
        {
            edge & nxt = vec[ fro_v[v] ][ fro_e[v] ];
            nxt.cap -= d;
            vec[v][nxt.rev].cap += d;
        }
    }
    return res;
}

int main()
{
    freopen("00in.txt", "r", stdin);
    scanf("%d%d%d%d%d", &n, &m, &s, &t, &flow);
    int ui, vi, wi, fi;
    for(int i = 0; i < m; ++i)
    {
        scanf("%d%d%d%d", &ui, &vi, &wi, &fi);
        add(ui, vi, fi, wi);
    }
    cout << min_cost_flow(s, t, flow) << '\n';
    return 0;
}

详见《挑战程序设计竞赛(第2版)》Page225

本想找个模板题试试,结果模板题都是最小费用最大流

Site by Baole Zhao | Powered by Hexo | theme PreciousJoy