[上下界有源汇最小流] BZOJ 2502: 清理雪道

Description

滑雪场坐落在FJ省西北部的若干座山上。
从空中鸟瞰,滑雪场可以看作一个有向无环图,每条弧代表一个斜坡(即雪道),弧的方向代表斜坡下降的方向。
你的团队负责每周定时清理雪道。你们拥有一架直升飞机,每次飞行可以从总部带一个人降落到滑雪场的某个地点,然后再飞回总部。从降落的地点出发,这个人可以顺着斜坡向下滑行,并清理他所经过的雪道。
由于每次飞行的耗费是固定的,为了最小化耗费,你想知道如何用最少的飞行次数才能完成清理雪道的任务。

Solution

作为一名蒟蒻根本不会网络流
补一补网络流。。。

首先这道题建图后,发现每条边的流量都是有上下界。
并且要求的是最小流。
先考虑如何求有源汇可行流。
首先先假设所有流都跑到了下界,那么就有了一个残量网络,并且这个残量网络是不满足流量平衡的。
考虑如何让其满足平衡。
对于一个节点 u ,设当前流出的流量减去流入的流量为Au,那么现在要做的就是构造一个网络(同样是流量不平衡的),使得这两个网络的并是流量平衡的。
所以说就是这个要构造的网络的每各节点 u 流入的流量减去流出的流量为Au
可以这么做:对一个点 u ,若Au>0就连一条 uT 的流量为 Au 边,否则就连一条 Su 的流量为 Au 边。
发现跑完最大流后(其实是希望能够跑满流),若存在解的话必然是合法的。
若有源汇的话,只要就连一条 TS 的流量为 + 边。可行流的流量就是 TS 的流量。
求最小流的话,考虑在残量网络上反向增广,跑 TS 的最大流,把前面可行流的流减掉这次跑的最大流就好了。

#include <bits/stdc++.h>
using namespace std;

const int N = 111;
const int INF = 1 << 28;

inline char get(void) {
    static char buf[100000], *S = buf, *T = buf;
    if (S == T) {
        T = (S = buf) + fread(buf, 1, 100000, stdin);
        if (S == T) return EOF;
    }
    return *S++;
}
template<typename T>
inline void read(T &x) {
    static char c; x = 0; int sgn = 0;
    for (c = get(); c < '0' || c > '9'; c = get()) if (c == '-') sgn = 1;
    for (; c >= '0' && c <= '9'; c = get()) x = x * 10 + c - '0';
    if (sgn) x = -x;
}

struct edge {
    int to, next, cap;
    edge (int t = 0, int n = 0, int c = 0):to(t), next(n), cap(c) {}
};
edge G[N * N * 2];
int Gcnt, n, m, x, clc;
int S, T, SS, TT, ans, SSS, TTT;
int head[N], A[N];
int d[N], cur[N], vis[N];
queue<int> Q;

inline void AddEdge(int from, int to, int cap) {
    G[++Gcnt] = edge(to, head[from], cap); head[from] = Gcnt;
    G[++Gcnt] = edge(from, head[to], 0); head[to] = Gcnt;
}
inline bool bfs(void) {
    Q.push(S); vis[S] = ++clc; d[S] = 0;
    int to, u; d[T] = INF;
    while (!Q.empty()) {
        u = Q.front(); Q.pop();
        for (int i = head[u]; i; i = G[i].next) {
            to = G[i].to;
            if (vis[to] != clc && G[i].cap) {
                d[to] = d[u] + 1;
                Q.push(to); vis[to] = clc;
            }
        }
    }
    return d[T] != INF;
}
inline int dfs(int u, int a) {
    if (u == T || !a) return a;
    int flow = 0, f;
    for (int &i = cur[u]; i; i = G[i].next) {
        edge &e = G[i];
        if (d[e.to] == d[u] + 1 && e.cap
            && (f = dfs(e.to, min(a, e.cap))) > 0) {
                flow += f; e.cap -= f;
                G[i ^ 1].cap += f; a -= f;
                if (!a) break;
          }
    }
    return flow;
}
inline int MaxFlow(void) {
    int flow = 0;
    while (bfs()) {
        for (int i = 1; i <= n + 4; i++) cur[i] = head[i];
        flow += dfs(S, INF);
    }
    return flow;
}
inline void Del(int from) {
    for (int i = head[from]; i; i = G[i].next)
        G[i].cap = G[i ^ 1].cap = 0;
}

int main(void) {
    freopen("1.in", "r", stdin);
    read(n); Gcnt = 1;
    for (int i = 1; i <= n; i++) {
        read(m);
        while (m--) {
            read(x); --A[i]; ++A[x];
            AddEdge(i, x, INF);
        }
    }
    S = n + 1; T = n + 2; SS = n + 3; TT = n + 4;
    for (int i = 1; i <= n; i++) {
        AddEdge(S, i, INF); AddEdge(i, T, INF);
    }
    for (int i = 1; i <= n; i++)
        if (A[i] < 0) AddEdge(i, TT, -A[i]);
        else AddEdge(SS, i, A[i]);
    AddEdge(T, S, INF);
    SSS = S; TTT = T;
    S = SS; T = TT;
    MaxFlow(); ans = G[Gcnt].cap;
    G[Gcnt].cap = G[Gcnt ^ 1].cap = 0;
    Del(S); Del(T);
    S = TTT; T = SSS;
    ans -= MaxFlow();
    printf("%d\n", ans);
    return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值