第五节 最小费用最大流

Contents

问题

P3381 【模板】最小费用最大流

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

输入格式
第一行包含四个正整数 N,M,S,T,分别表示点的个数、有向边的个数、源点序号、汇点序号。
接下来 M 行每行包含四个正整数 u_i,v_i,w_i,f_i,表示第 i 条有向边从 u_i 出发,到达 v_i,边权为 w_i,单位流量的费用为 f_i

输出格式
一行两个整数,依次为最大流量和在最大流量情况下的最小费用。

朴素spfa

#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
const long long INF = 1e18;
const int N = 5e3 + 50;
const int MAXM = 6e5 + 50;
int n, m, S, T;

int head[N], tot = 1;
struct Edge
{
    int nex, to, w, c;
}e[2 * MAXM];
void addedge(int a, int b, int c, int d)
{
    e[++tot] = (Edge) {head[a], b, c, d};
    head[a] = tot;
}
void add(int a, int b, int c, int d) {
    addedge(a, b, c, d);
    addedge(b, a, 0, -d);
}

struct MCMF {
    queue<int> q;
    int n, S, T;
    long long maxflow, mincost;
    int pre[N], last[N], vis[N], flow[N];
    long long dis[N];

    void init(int _n, int _S, int _T) {
        n = _n; S = _S; T = _T;
        maxflow = mincost = 0;
        memset(head, 0, sizeof(head));
        tot = 1;
    }
    int spfa() {
        for (int i = 1; i <= n; i++) {
            dis[i] = INF; vis[i] = 0;
        }
        dis[S] = 0; vis[S] = 1;
        flow[S] = 1e9; pre[T] = 0;
        while(!q.empty()) q.pop(); q.push(S);
        while(!q.empty())
        {
            int now = q.front();
            q.pop(); vis[now] = 0;
            for(int i = head[now]; i; i = e[i].nex)
            {
                int to = e[i].to;
                if(e[i].w && dis[to] > dis[now] + e[i].c)
                {
                    dis[to] = dis[now] + e[i].c;
                    pre[to] = now;
                    last[to] = i;
                    flow[to] = min(e[i].w, flow[now]);
                    if(!vis[to])
                    {
                        vis[to] = 1;
                        q.push(to);
                    }
                }
            }
        }
        return pre[T] > 0;
    }

    void solve() {
        while(spfa()) {
            maxflow += flow[T];
            mincost += 1LL * flow[T] * dis[T];
            for(int now = T; now != S; now = pre[now])
            {
                e[last[now]].w -= flow[T];
                e[last[now]^1].w += flow[T];
            }
        }
    }
}mcmf;

int main()
{
    scanf("%d%d%d%d", &n, &m, &S, &T);
    mcmf.init(n, S, T);
    for(int i = 1, a, b, c, d; i <= m; i++)
    {
        scanf("%d%d%d%d", &a, &b, &c, &d);
        add(a, b, c, d);
    }
    mcmf.solve();
    printf("%lld %lld\n", mcmf.maxflow, mcmf.mincost);
    return 0;
}

Dijkstra优化

#include <bits/stdc++.h>
using namespace std;
typedef pair<long long, int> pii;
typedef long long ll;
#define mp make_pair

const int N = 5e3 + 50;
const int MAXM = 6e5 + 50;
const long long INF = 1e18;
int n, m, S, T;

int head[N], tot = 1;
struct Edge
{
    int nex, to, w, c;
}e[2 * MAXM];
void addedge(int a, int b, int c, int d)
{
    e[++tot] = (Edge) {head[a], b, c, d};
    head[a] = tot;
}
void add(int a, int b, int c, int d) {
    addedge(a, b, c, d);
    addedge(b, a, 0, -d);
}

struct MCMF {
    long long maxflow, mincost;
    int n, S, T;

    void init(int _n, int _S, int _T) {
        n = _n; S = _S; T = _T;
        maxflow = mincost = 0;
        memset(head, 0, sizeof(head));
        tot = 1;
    }

    int vis[N], tim[N];
    long long d[N];
    queue<int> q;
    void spfa(int u) {
        for (int i = 1; i <= n; i++) {
            d[i] = INF; vis[i] = 0;
        }
        d[u] = 0; vis[u] = 1;
        q.push(u);
        while (!q.empty()) {
            int v = q.front();
            q.pop(); vis[v] = 0;
            if (++tim[v] >= n) {
                return ;
            }
            for (int i = head[v]; i; i = e[i].nex) {
                int to = e[i].to;
                if (e[i].w && d[to] > d[v] + e[i].c) {
                    d[to] = d[v] + e[i].c;
                    if (!vis[to]) {
                        vis[to] = 1;
                        q.push(to);
                    }
                }
            }
        }
    }

    long long dis[N];
    int pre[N], last[N], flow[N];
    priority_queue<pii, vector<pii>, greater<pii> > pq;
    bool Dijkstra() {
        for (int i = 1; i <= n; i++) {
            dis[i] = INF; vis[i] = 0;
        }
        while(!pq.empty()) pq.pop();
        dis[S] = 0; pre[T] = 0;
        flow[S] = 1e9;
        pq.push(mp(0, S));
        while(!pq.empty()) {
            pii t = pq.top(); pq.pop();
            int id = t.second;
            if(vis[id]) continue;
            vis[id] = 1;
            for(int i = head[id]; i; i = e[i].nex) {
                int to = e[i].to;
                long long len = t.first + d[id] - d[to] + e[i].c;
                if(e[i].w && dis[to] > len) {
                    dis[to] = len;
                    pq.push(mp(dis[to], to));
                    flow[to] = min(e[i].w, flow[id]);
                    pre[to] = id;
                    last[to] = i;
                }
            }
        }
        return pre[T] > 0;
    }

    void solve() {
        spfa(S);
        while (Dijkstra()) {
            maxflow += flow[T];
            mincost += 1LL * flow[T] * (d[T] + dis[T]);
            for(int now = T; now != S; now = pre[now])
            {
                e[last[now]].w -= flow[T];
                e[last[now]^1].w += flow[T];
            }
            for (int i = 1; i <= n; i++) {
                d[i] = min(d[i] + dis[i], INF);
            }
        }
    }
}mcmf;

int main(int argc, const char * argv[]) {
    scanf("%d%d%d%d", &n, &m, &S, &T);
    mcmf.init(n, S, T);
    for(int i = 1, a, b, c, d; i <= m; i++)
    {
        scanf("%d%d%d%d", &a, &b, &c, &d);
        add(a, b, c, d);
    }
    mcmf.solve();
    printf("%lld %lld\n", mcmf.maxflow, mcmf.mincost);
    return 0;
}

应用

例题:P2045 方格取数加强版

题目大意:

给出一个 n\times n 的矩阵,每一格有一个非负整数 A_{ij},(A_{ij} \le 1000) 现在从 (1,1) 出发,可以往右或者往下走,最后到达 (n,n),每达到一格,把该格子的数取出来,该格子的数就变成 0,这样一共走 K 次,现在要求 K次所达到的方格的数的和最大。

分析:

  1. 超级源点和超级汇点分别连向(1,1)(n , n),容量为k,费用为0,仅表示一共可以走k次;
  2. 对于方格中的每个点,拆成两个点,分别为入点和出点。入点和出点之间连两条边,一条容量为 1,费用为点的权值,表示每个点的数只可以取一次;另一条容量为 \text{inf},费用为 0,仅表示可以经过无数次;
  3. 对于在其下方或右边点的点,连一条容量为 \text{inf},费用为 0 的边,表示且仅表示一种联通的关系。
  4. 把费用设为负数,最后再取相反数即可。
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇