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

題目鏈接 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萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章