清理雪道【有源汇有上下界最小流】

题目链接 P4843


  首先,我们可以知道,直接用有源汇有上下界网络流求解出来的答案是介于最大流到最小流中间的某个可能值,所以其中肯定是需要一些操作来使得它变成最小值的。

  首先,固定源汇点,不是所有点都是可以变成源汇点的,所以我单独开一个s和t来充当源汇点。因为原图是DAG,所以,可以使用我们将源点链接那些入度为0的点,让出度为0的点去链接汇点,这样必定是最贪心的。

  然后,就是要做一个思考,我们按照有源汇有上下界跑完之后,我们将这个残余网络的S看成原先的t,将T看成原先的s,我们从t到s跑一个反向边的最大流,在用原先的值减去它,是不是就可以得到一个最小流了呢?

模型:现在的网络有一个源点s和汇点t,求出一个流使得源点的总流出量等于汇点的总流入量,其他的点满足流量守恒,而且每条边的流量满足上界和下界限制.在这些前提下要求总流量最小.

依然是先跑出一个有源汇可行流.这时候的流也不一定是最小的.假如我们能在残量网络上找到一条s-t的路径使得去掉这条路径上的流量之后仍然满足流量下限,我们就可以得到一个更小的流.好像我们并没有什么算法可以“找到尽可能多的能够去除流量的路径”

这时候需要我们再理解一下网络流的反向边.反向边的流量增加等价于正向边的的流量减少.因此我们在残量网络上找出t到s的流就相当于减小了s到t的流,因此我们在跑出可行流的残量网络上跑t-s最大流,用可行流的大小减去这一次t-s最大流的大小就是最小流的大小.(t-s最大流其实是尽量缩减s-t方向的流).

问题:会不会使流量缩减到不满足流量下限?

不会.和有源汇有上下限的最大流一样,我们之前从每条边上拿出了大小等于流量下限的流量构成初始流,这些流量不在我们建出的图中.最极端的情况是缩减到所有边的流量等于流量下限,不会更小了.

#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#include <bitset>
#include <unordered_map>
#include <unordered_set>
#define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
#define e 2.718281828459045
#define INF 0x7f7f7f7f
#define HalF (l + r)>>1
#define lsn rt<<1
#define rsn rt<<1|1
#define Lson lsn, l, mid
#define Rson rsn, mid+1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
#define myself rt, l, r
using namespace std;
typedef unsigned long long ull;
typedef unsigned int uit;
typedef long long ll;
const int maxN = 107, maxM = 2e4 + 7;
int N, head[maxN], cnt, du[maxN], _Index, In_du[maxN], Out_du[maxN];
struct Eddge
{
    int nex, to, flow;
    Eddge(int a=-1, int b=0, int c=0):nex(a), to(b), flow(c) {}
}edge[maxM];
inline void addEddge(int u, int v, int f)
{
    edge[cnt] = Eddge(head[u], v, f);
    head[u] = cnt++;
}
inline void _add(int u, int v, int f) { addEddge(u, v, f); addEddge(v, u, 0); }
struct ISAP
{
    int S, T, gap[maxN], cur[maxN], deep[maxN], que[maxN], ql, qr;
    inline void init()
    {
        for(int i=0; i<=T + 2; i++)
        {
            gap[i] = deep[i] = 0;
            cur[i] = head[i];
        }
        ++gap[deep[T] = 1];
        que[ql = qr = 1] = T;
        while(ql <= qr)
        {
            int u = que[ql ++];
            for(int i=head[u], v; ~i; i=edge[i].nex)
            {
                v = edge[i].to;
                if(!deep[v]) { ++gap[deep[v] = deep[u] + 1]; que[++qr] = v; }
            }
        }
    }
    inline int aug(int u, int Flow)
    {
        if(u == T) return Flow;
        int flow = 0;
        for(int &i = cur[u], v; ~i; i=edge[i].nex)
        {
            v = edge[i].to;
            if(deep[u] == deep[v] + 1)
            {
                int tmp = aug(v, min(edge[i].flow, Flow));
                flow += tmp; Flow -= tmp; edge[i].flow -= tmp; edge[i ^ 1].flow += tmp;
                if(!Flow) return flow;
            }
        }
        if(!(--gap[deep[u]])) deep[S] = T + 2;
        ++gap[++deep[u]]; cur[u] = head[u];
        return flow;
    }
    inline int Max_Flow()
    {
        init();
        int ret = aug(S, INF);
        while(deep[S] <= T + 1) ret += aug(S, INF);
        return ret;
    }
} mf;
int s, t;
inline void init()
{
    cnt = 0; s = 0; t = N + 1; mf.S = N + 2; mf.T = N + 3;
    for(int i=0; i<=N + 3; i++) { head[i] = -1; du[i] = 0; }
}
int main()
{
    scanf("%d", &N);
    init();
    for(int i=1, Mi, id; i<=N; i++)
    {
        scanf("%d", &Mi); Out_du[i] += Mi;
        for(int j=1; j<=Mi; j++)
        {
            scanf("%d", &id);
            _add(i, id, INF);
            du[i] -= 1; du[id] += 1; In_du[id]++;
        }
    }
    for(int i=1; i<=N; i++)
    {
        if(!In_du[i]) _add(s, i, INF);
        if(!Out_du[i]) _add(i, t, INF);
    }
    for(int i=0; i<=t; i++)
    {
        if(du[i] > 0)
        {
            _add(mf.S, i, du[i]);
        }
        else if(du[i] < 0)
        {
            _add(i, mf.T, -du[i]);
        }
    }
    _add(t, s, INF);
    mf.Max_Flow();
    int tmp = edge[cnt - 1].flow;
    head[mf.S] = head[mf.T] = -1;
    mf.S = t; mf.T = s;
    printf("%d\n", tmp - mf.Max_Flow());
    return 0;
}

 

发布了805 篇原创文章 · 获赞 966 · 访问量 9万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章