@总结 - 8@ 上下界网络流等一类网络流问题

目录

  • @0 - 参考资料@
  • @1 - 问题引入@
  • @2 - 上下界可行流@
  • @3 - 上下界最大流/最小流@
  • @4 - 上下界费用流@
  • @5 - 带负环的最小费用流@
  • @6 - 例题与参考代码实现@
  • @7 - 一些类似的杂题@

@0 - 参考资料@

menci 的博客

liu_runda 的博客

@1 - 问题引入@

我们知道,通常情况下,一个合法的流应该具有如下几个性质:

(1)(除源点汇点以外)流量守恒:\(\sum f(i, u) = \sum f(u, j)\)。

(2)斜对称性:\(f(u, v) = -f(v, u)\)。

(3)容量限制:\(f(u, v) \le c(u, v)\)。

但在某些问题中,我们还要求边的流量有下界,即 \(l(u, v) \le f(u, v) \le c(u, v)\)。

我们需要将这种流量带有上下界的网络流进行模型的转化,使得可以使用通常的网络流算法解决。

@2 - 上下界可行流@

什么是可行流?简单说即不包含源汇的,满足流量守恒、斜对称性、流量上下界的流。

如果不包含下界,零流就是一个可行流。

根据定义我们有 \(f(u, v) = l(u, v) + f‘(u, v)\),其中 \(0 \le f‘(u, v) \le c(u, v) - l(u, v)\),可以发现 \(f‘(u, v)\) 是一个没有下界的流。

我们将原来的边 \((u, v)\) 的上界改成 \(c(u, v) - l(u, v)\),再进行下一步的处理。

我们考虑用一个等价的东西代替 \(l(u, v)\) 这部分流的。

考虑 \(l(u, v)\) 这部分流,它只会对 “流量守恒” 这一个条件造成影响:它贡献了点 \(v\) 流量为 \(l(u, v)\) 的入流,贡献了点 \(u\) 流量为 \(l(u, v)\) 的出流。

我们新建源点 \(ss\),汇点 \(tt\)。对于边 \((u, v)\),我们新建边 \((ss, v), (u, tt)\),使得它们的容量都为 \(l(u, v)\)。

这样,假如所有的 \((ss, v)\), \((u, tt)\) 都满流,它们就可以起到和 \(l(u, v)\) 一样的作用。

找出 \(ss\) 到 \(tt\) 的最大流即可,如果满流说明有一个可行解。此时边 \((u, v)\) 的流量就是 \(f‘(u, v)\)(不是 \(f(u, v)\) 哦,\(f(u, v) = l(u, v) + f‘(u, v)\))。

这样的确就是一个完整的算法了。但是我们还可以进行建图上的优化。

如果同时有边 \((ss, u), (u, tt)\),可以合并成一条边。如果有多条 \((ss, u)/(u, tt)\),也可以合并成一条边。

具体的话,可以统计 \(ss\) 到 \(u\) 的容量和 \(-\) \(u\) 到 \(tt\) 的容量和,根据其正负以及大小来建边。

@3 - 上下界最大流/最小流@

考虑求解带源汇的可行流(因为最大流/最小流必须要在有源汇的情况下才能被定义):

我们汇点 \(t\) 向源点 \(s\) 连一条容量为 \(inf\) 的边即可。

此时因为流量守恒, \(f‘(t, s)\) 就等于当前可行流中 \(s\) 到 \(t\) 的流量。

考虑求解最大流:

直接源点 \(s\) 向汇点 \(t\) 增广即可。

首先,\(ss\) 没有入边,\(tt\) 没有出边,增广路不会经过它们俩。

然后,因为斜对称性, \((t, s)\) 有一个流量为 \(-f‘(t, s)\) 的反向边。增广的时候必然会经过这个反向边,就把初始可行流中 \(s\) 到 \(t\) 的流量统计进去了。

而且因此,也不需要特意去加上某些边的流量下界(因为这个是包含下界的可行流哦)。

最后,因为我们的下界是转换成等价形式了。因此如果不修改 \(ss, tt\) 的流量,就不会出现不满足下界的情况。

考虑求解最小流(没有下界的话,最小流就是零流):

先去掉 \((t, s)\),汇点 \(t\) 向源点 \(s\) 增广。最后用 \(f‘(t, s)\) \(-\) 增广得到的最大流就是答案。

t 向 s 增广即退流操作,退的越多自然流量就越小。其他的和上面差不多。

@4 - 上下界费用流@

(先不考虑负环的问题)

考虑求解无源汇的可行流。

建图部分和最大流差不多,在最后 \(ss\) 向 \(tt\) 跑最小费用最大流即可。

考虑求解有源汇的最小费用最大流。

\(t\) 向 \(s\) 连容量 \(inf\),费用 \(0\) 的边,跑最小费用最大流,再 \(s\) 向 \(t\) 求最小费用最大流。

考虑求解有源汇的最小费用流。

\(t\) 向 \(s\) 连容量 \(inf\),费用 \(0\) 的边,跑最小费用最大流,再 \(s\) 向 \(t\) 求最小费用流。

其实和上面的最大流问题模型差不多。

@5 - 带负环的最小费用流@

这个……尽管和上下界网络流已经没关系了,但是鉴于它们的思路有一定的相似性,我还是在这里提一下。

考虑求解最小费用循环流。

什么是最小费用循环流?实际上就是合法的,费用最小的无源汇的流。

一个算法是:找到一个负权环,将它加入答案。可以发现这个算法效率不高。

一个想法是:我们可以先贪心地选择所有的负权边,再花费最小代价将它调整为合法的流(不会证明,直观上是对的)。

调整这一步,是不是比较类似于上下界网络流呢?

但是,和上下界网络流不同的是,这里是预先流满(可以取消选择),而上下界网络流是强制流满(不能取消选择)。

怎么实现撤回呢?根据斜对称性,一条边的流的减少等价于它反向边的流的增加。

所以我们这样来建图:

对于负权边 \((u, v)\),连 \((ss, v), (u, tt)\),费用为 0;连 \((v, u)\),费用为 \(-w(u, v)\)。三条边的容量都为 \(c(u, v)\)。

对于其他边,保持不变。

最后 \(ss\) 向 \(tt\) 跑最小费用最大流,答案为 最大流的费用 + 所有的负权边的权值和。

建图方面,是不是和上下界网络流也有一定的共同点?

这是一个通用套路。

带源汇的话,一样是 \(t\) 向 \(s\) 连容量 \(inf\),费用 \(0\) 的边。

这也是一个通用套路。

@6 - 例题与参考代码实现@

上下界可行流模板题 sgu 194:Reactor Cooling

【sgu 都搬去和 cf 一个网站了……然而 vjudge 还是没有更新】

【51nod 换网址了……然而 vjudge 还是没有更新】

参考代码:

#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXV = 200 + 5;
const int MAXE = 2*200*200 + 5;
const int INF = 1<<30;
struct flow_graph{
    struct edge{
        int to, flow, cap;
        edge *nxt, *rev;
    }e[MAXE], *adj[MAXV], *ecnt;
    int d[MAXV], vd[MAXV], s, t;
    void init() {
        ecnt = &e[0];
        for(int i=0;i<MAXV;i++)
            adj[i] = NULL, d[i] = vd[i] = 0;
    }
    void addedge(int u, int v, int c) {
        edge *p = (++ecnt), *q = (++ecnt);
        p->to = v, p->cap = c, p->flow = 0;
        p->nxt = adj[u], adj[u] = p;
        q->to = u, q->cap = 0, q->flow = 0;
        q->nxt = adj[v], adj[v] = q;
        p->rev = q, q->rev = p;
    }
    int aug(int x, int tot) {
        if( x == t ) return tot;
        int mind = t + 1, sum = 0;
        for(edge *p=adj[x];p;p=p->nxt) {
            if( p->cap > p->flow ) {
                if( d[x] == d[p->to] + 1 ) {
                    int del = aug(p->to, min(tot-sum, p->cap-p->flow));
                    p->flow += del, p->rev->flow -= del, sum += del;
                    if( sum == tot || d[s] > t + 1 ) return sum;
                }
                mind = min(mind, d[p->to]);
            }
        }
        if( !sum ) {
            vd[d[x]]--;
            if( !vd[d[x]] ) {
                d[s] = t + 2;
                return sum;
            }
            d[x] = mind + 1;
            vd[d[x]]++;
        }
        return sum;
    }
    int max_flow(int _s, int _t) {
        int flow = 0; s = _s, t = _t;
        while( d[s] <= t + 1 )
            flow += aug(s, INF);
        return flow;
    }
}G;
struct edge{
    int u, v, l, c;
}e[MAXE];
int deg[MAXV];
int main() {
    int N, M; scanf("%d%d", &N, &M); G.init();
    for(int i=1;i<=M;i++) {
        scanf("%d%d%d%d", &e[i].u, &e[i].v, &e[i].l, &e[i].c);
        deg[e[i].u] -= e[i].l, deg[e[i].v] += e[i].l; G.addedge(e[i].u, e[i].v, e[i].c - e[i].l);
    }
    int tot = 0;
    for(int i=1;i<=N;i++)
        if( deg[i] < 0 ) G.addedge(i, N + 1, -deg[i]);
        else if( deg[i] > 0 ) G.addedge(0, i, deg[i]), tot += deg[i];
    if( G.max_flow(0, N + 1) == tot ) {
        puts("YES");
        for(int i=1;i<=M;i++)
            printf("%d\n", e[i].l + G.e[2*i-1].flow);
    }
    else puts("NO");
}

上下界最大流问题 zoj 3229:Shoot the Bullet(东方文花帖)

建模比较简单,为了不剧透,附在代码最末。

参考代码:

#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXV = 1500 + 5;
const int MAXE = MAXV*100 + 5;
const int INF = int(1E9);
struct FlowGraph{
    struct edge{
        int to, flow, cap;
        edge *nxt, *rev;
    }edges[MAXE], *adj[MAXV], *ecnt;
    int s, t, n, d[MAXV], vd[MAXV];
    void init() {
        ecnt = &edges[0];
        for(int i=0;i<MAXV;i++) adj[i] = NULL;
    }
    void addedge(int u, int v, int c) {
        edge *p = (++ecnt), *q = (++ecnt);
        p->to = v, p->flow = 0, p->cap = c;
        p->nxt = adj[u], adj[u] = p;
        q->to = u, q->flow = 0, q->cap = 0;
        q->nxt = adj[v], adj[v] = q;
        p->rev = q, q->rev = p;
    }
    int aug(int x, int tot) {
        if( x == t ) return tot;
        int sum = 0, mind = n + 1;
        for(edge *p=adj[x];p;p=p->nxt) {
            if( p->cap > p->flow ) {
                if( d[p->to] + 1 == d[x] ) {
                    int del = aug(p->to, min(tot-sum, p->cap-p->flow));
                    p->flow += del, p->rev->flow -= del, sum += del;
                    if( sum == tot || d[s] > n ) return sum;
                }
                mind = min(mind, d[p->to]);
            }
        }
        if( sum == 0 ) {
            vd[d[x]]--;
            if( vd[d[x]] == 0 )
                d[s] = n + 1;
            d[x] = mind + 1;
            vd[d[x]]++;
        }
        return sum;
    }
    int max_flow(int _s, int _t, int _n) {
        s = _s, t = _t, n = _n;
        for(int i=0;i<MAXV;i++) d[i] = vd[i] = 0;
        int flow = 0;
        while( d[s] <= n )
            flow += aug(s, INF);
        return flow;
    }
}G;
int deg[MAXV], C[MAXV], L[500 + 5][1000 + 5];
int main() {
    int n, m;
    while( scanf("%d%d", &n, &m) == 2 ) {
        G.init(); int s = n + m + 1, t = n + m + 2;
        for(int i=1;i<=m;i++) {
            int x; scanf("%d", &x);
            G.addedge(i, t, INF);
            deg[t] += x, deg[i] -= x;
        }
        for(int i=1;i<=n;i++) {
            int D; scanf("%d%d", &C[i], &D);
            G.addedge(s, m + i, D);
            for(int j=1;j<=C[i];j++) {
                int T, R; scanf("%d%d%d", &T, &L[i][j], &R), T++;
                G.addedge(m + i, T, R - L[i][j]);
                deg[T] += L[i][j], deg[m + i] -= L[i][j];
            }
        }
        int tot = 0;
        for(int i=1;i<=n+m+2;i++) {
            if( deg[i] > 0 ) G.addedge(0, i, deg[i]), tot += deg[i];
            if( deg[i] < 0 ) G.addedge(i, n + m + 3, -deg[i]);
            deg[i] = 0;
        }
        G.addedge(t, s, INF);
        if( G.max_flow(0, n + m + 3, n + m + 3) == tot ) {
            printf("%d\n", G.max_flow(s, t, n + m + 3));
            for(int i=1;i<=n;i++) {
                int j = C[i];
                for(FlowGraph::edge *p=G.adj[m + i];p;p=p->nxt)
                    if( p->to <= m && p->to >= 1 ) L[i][j] += p->flow, j--;
            }
            for(int i=1;i<=n;i++)
                for(int j=1;j<=C[i];j++)
                    printf("%d\n", L[i][j]);
        }
        else printf("-1\n");
        puts("");
    }
}//MLE 报 Segmentation Fault, RE 也报 Segmentation Fault……
/*
建成二分图。
左边一排 n 个点表示 n 天,由源点连过来,容量为 D。
右边一排 m 个点表示 m 个女孩,连向汇点,下界为 G。
第 i 天向 Ci 个女孩连流量在 [Lij, Rij] 的边。
*/

上下界最小流问题 bzoj 2502:清理雪道

一样的,建模附在最后面。

参考代码:

#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXV = 100 + 5;
const int MAXE = MAXV*MAXV + 5;
const int INF = int(1E9);
struct FlowGraph{
    struct edge{
        int to, flow, cap;
        edge *nxt, *rev;
    }edges[MAXE], *adj[MAXV], *ecnt;
    int s, t, n, d[MAXV], vd[MAXV];
    void init() {
        ecnt = &edges[0];
        for(int i=0;i<MAXV;i++) adj[i] = NULL;
    }
    void addedge(int u, int v, int c) {
        edge *p = (++ecnt), *q = (++ecnt);
        p->to = v, p->flow = 0, p->cap = c;
        p->nxt = adj[u], adj[u] = p;
        q->to = u, q->flow = 0, q->cap = 0;
        q->nxt = adj[v], adj[v] = q;
        p->rev = q, q->rev = p;
    }
    int aug(int x, int tot) {
        if( x == t ) return tot;
        int sum = 0, mind = n + 1;
        for(edge *p=adj[x];p;p=p->nxt) {
            if( p->cap > p->flow ) {
                if( d[p->to] + 1 == d[x] ) {
                    int del = aug(p->to, min(tot-sum, p->cap-p->flow));
                    p->flow += del, p->rev->flow -= del, sum += del;
                    if( sum == tot || d[s] > n ) return sum;
                }
                mind = min(mind, d[p->to]);
            }
        }
        if( sum == 0 ) {
            vd[d[x]]--;
            if( vd[d[x]] == 0 )
                d[s] = n + 1;
            d[x] = mind + 1;
            vd[d[x]]++;
        }
        return sum;
    }
    int max_flow(int _s, int _t, int _n) {
        s = _s, t = _t, n = _n;
        for(int i=0;i<MAXV;i++) d[i] = vd[i] = 0;
        int flow = 0;
        while( d[s] <= n )
            flow += aug(s, INF);
        return flow;
    }
}G;
int deg[MAXV];
int main() {
    G.init(); int N; scanf("%d", &N);
    int s = N + 1, t = N + 2, ss = 0, tt = N + 3;
    for(int i=1;i<=N;i++) {
        int K; scanf("%d", &K);
        deg[i] += K;
        for(int j=1;j<=K;j++) {
            int B; scanf("%d", &B);
            G.addedge(i, B, INF); deg[B]--;
        }
        G.addedge(s, i, INF);
        G.addedge(i, t, INF);
    }
    for(int i=1;i<=N;i++) {
        if( deg[i] < 0 ) G.addedge(ss, i, -deg[i]);
        if( deg[i] > 0 ) G.addedge(i, tt, deg[i]);
    }
    G.addedge(t, s, INF);
    G.max_flow(ss, tt, tt);
    int ans = G.adj[t]->flow;
    G.adj[s] = G.adj[s]->nxt, G.adj[t] = G.adj[t]->nxt;
    printf("%d\n", ans - G.max_flow(t, s, tt));
}
/*
每个点都可以作为起点/终点:源点连每个点/每个点连汇点。
必须经过即流量下界为 1。

这或许启发我们 DAG 中的最小路径覆盖/有向图中的最小环覆盖 也可以转换为上下界网络流的模型。
*/

上下界最小费用流问题 bzoj 3876: [Ahoi2014&Jsoi2014]支线剧情

建模附在代码最后。

参考代码:

#include<queue>
#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXV = 400 + 5;
const int MAXE = 20000 + 5;
const int INF = int(1E9);
struct FlowGraph{
    struct edge{
        int to, flow, cap, dis;
        edge *nxt, *rev;
    }edges[MAXE], *adj[MAXV], *cur[MAXV], *ecnt;
    int s, t, cost, dist[MAXV];
    void init() {
        ecnt = &edges[0];
        for(int i=0;i<MAXV;i++) adj[i] = NULL;
    }
    void addedge(int u, int v, int c, int w) {
        edge *p = (++ecnt), *q = (++ecnt);
        p->to = v, p->flow = 0, p->cap = c, p->dis = w;
        p->nxt = adj[u], adj[u] = p;
        q->to = u, q->flow = 0, q->cap = 0, q->dis = -w;
        q->nxt = adj[v], adj[v] = q;
        p->rev = q, q->rev = p;
    }
    bool inque[MAXV];
    bool relabel() {
        queue<int>que;
        for(int i=0;i<MAXV;i++) dist[i] = INF, cur[i] = adj[i];
        que.push(s); dist[s] = 0, inque[s] = true;
        while( !que.empty() ) {
            int f = que.front(); que.pop(); inque[f] = false;
            for(edge *p=adj[f];p;p=p->nxt) {
                if( p->cap > p->flow && dist[f] + p->dis < dist[p->to] ) {
                    dist[p->to] = dist[f] + p->dis;
                    if( !inque[p->to] ) {
                        que.push(p->to);
                        inque[p->to] = true;
                    }
                }
            }
        }
        return !(dist[t] == INF);
    }
    bool vis[MAXV];
    int aug(int x, int tot) {
        if( x == t ) {
            cost += tot*dist[x];
            return tot;
        }
        int sum = 0; vis[x] = true;
        for(edge *&p=cur[x];p;p=p->nxt) {
            if( p->cap > p->flow && !vis[p->to] && dist[p->to] == dist[x] + p->dis ) {
                int del = aug(p->to, min(tot-sum, p->cap-p->flow));
                p->flow += del, p->rev->flow -= del, sum += del;
                if( sum == tot ) break;
            }
        }
        vis[x] = false;
        return sum;
    }
    int min_cost_max_flow(int _s, int _t) {
        s = _s, t = _t; int flow = 0; cost = 0;
        while( relabel() )
            flow += aug(s, INF);
        return flow;
    }
}G;
int deg[MAXV];
int main() {
    G.init(); int N; scanf("%d", &N);
    int s = N + 1, t = N + 2, ss = 0, tt = N + 3, ans = 0;
    for(int i=1;i<=N;i++) {
        int K; scanf("%d", &K);
        deg[i] += K;
        for(int j=1;j<=K;j++) {
            int B, T; scanf("%d%d", &B, &T);
            G.addedge(i, B, INF, T); deg[B]--;
            ans += T;
        }
        G.addedge(i, t, INF, 0);
    }
    G.addedge(s, 1, INF, 0);
    for(int i=1;i<=N;i++) {
        if( deg[i] < 0 ) G.addedge(ss, i, -deg[i], 0);
        if( deg[i] > 0 ) G.addedge(i, tt, deg[i], 0);
    }
    G.addedge(t, s, INF, 0);
    G.min_cost_max_flow(ss, tt);
    printf("%d\n", ans + G.cost);
}
/*
和上一题比较类似,只是给定了边的费用,并且规定起点必须在 1。
注意因为原图(包含源汇 s, t 的那个图)是一个 DAG,所以从 ss 出发的流必然会经过 s 才会回到 tt。
即我们不需要最后再从 s 开始增广一遍。
*/

@7 - 一些类似的杂题@

poj 1637:Sightseeing tour(混合图欧拉回路问题)

这是一个比较经典的问题。思路是:尝试给无向边定向,使得每个点的入度等于其出度。

入度等于出度?是不是很像网络流中的流量守恒。

我们先随机给无向边定向,再进行调整(把某些无向边反向)。

考虑反向一条无向边会发生什么:

相当于这条边的流量-1,它反向边的流量+1。

相当于这条边的流量-2。

因为是欧拉回路,每个点的度数必须要为偶数。我们不妨给所有边的流量 / 2。

这样,调整一条边变成了给这条边的流量-1。

这就十分网络流了。直接按照上下界网络流的思路来使得它流量守恒。

一份参考代码。

#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXV = 200 + 5;
const int MAXE = 5000 + 5;
const int INF = 1<<30;
struct FlowGraph{
    struct edge{
        int to, flow, cap;
        edge *nxt, *rev;
    }edges[MAXE], *adj[MAXV], *ecnt;
    int s, t, d[MAXV], vd[MAXV];
    void init() {
        ecnt = &edges[0];
        for(int i=0;i<MAXV;i++)
            adj[i] = NULL, d[i] = vd[i] = 0;
    }
    void addedge(int u, int v, int c) {
        edge *p = (++ecnt), *q = (++ecnt);
        p->to = v, p->cap = c, p->flow = 0;
        p->nxt = adj[u], adj[u] = p;
        q->to = u, q->cap = 0, q->flow = 0;
        q->nxt = adj[v], adj[v] = q;
        p->rev = q, q->rev = p;
        //printf("%d %d %d\n", u, v, c);
    }
    int aug(int x, int tot) {
        //printf("%d %d %d %d\n", x, s, t, tot);
        if( x == t ) return tot;
        int mind = t, sum = 0;
        for(edge *p=adj[x];p;p=p->nxt) {
            if( p->cap > p->flow ) {
                if( d[x] == d[p->to] + 1 ) {
                    int del = aug(p->to, min(tot-sum, p->cap-p->flow));
                    p->flow += del, p->rev->flow -= del, sum += del;
                    if( sum == tot || vd[s] > t ) return sum;
                }
                mind = min(mind, d[p->to]);
            }
        }
        if( !sum ) {
            vd[d[x]]--;
            if( !vd[d[x]] ) {
                d[s] = t + 1;
                return sum;
            }
            d[x] = mind + 1;
            vd[d[x]]++;
        }
        return sum;
    }
    int max_flow(int _s, int _t) {
        int flow = 0; s = _s, t = _t;
        while( d[s] <= t )
            flow += aug(s, INF);
        //printf("%d\n", flow);
        return flow;
    }
}G;
int deg[MAXV];
void solve() {
    G.init(); int m, s, ss, tt;
    scanf("%d%d", &m, &s);
    for(int i=1;i<=m;i++) deg[i] = 0;
    for(int i=1;i<=s;i++) {
        int x, y, d; scanf("%d%d%d", &x, &y, &d);
        if( d == 0 ) G.addedge(x, y, 1);
        deg[x]++, deg[y]--;
    }
    for(int i=1;i<=m;i++) {
        if( deg[i] % 2 ) {
            puts("impossible");
            return ;
        }
        deg[i] /= 2;
    }
    int tot = 0; ss = 0, tt = m + 1;
    for(int i=1;i<=m;i++)
        if( deg[i] > 0 ) G.addedge(ss, i, deg[i]), tot += deg[i];
        else if( deg[i] < 0 ) G.addedge(i, tt, -deg[i]);
    if( G.max_flow(ss, tt) == tot ) puts("possible");
    else puts("impossible");
}
int main() {
    int n; scanf("%d", &n);
    for(int i=1;i<=n;i++) solve();
}

codeforces 708D:Incorrect Flow

也是一道涉及预流,然后调整流使得其满足网络流性质的题。

这里可以戳到我的题解

原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/10383398.html

时间: 2024-07-28 13:57:40

@总结 - 8@ 上下界网络流等一类网络流问题的相关文章

【网络流补全计划】Part.Ⅲ有上下界的网络流

本来心情就非常糟糕调月下毛景树把我最后一点写代码的心情调没了 放弃 开始补全网络流. 之前学了普通最大流,那么现在开始补有上下界的网络流. 在普通最大流中,网络里的每一条边都只有流量的上界即边的容量,而引入上下界网络流之后,每个边不但有一个容量,还有一个流量下界. 我们令B(u,v)表示边(u,v)的下界.于是我们可以有表达式: B(u,v)≤f(u,v)≤C(u,v) 有这个式子可以得到 0≤f(u,v)≤C(u,v)?B(u,v) 至此,我们可以将有上下界的网络流分为几种问题来对待,接下来就

ZOJ 2314 Reactor Cooling 无源汇有上下界网络流

题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=1314 题意:给出N个点,M条边的有向图,每条边的有上下界规定,问是否存在一个可行流满足条件,如果满足输出YES并输出每条边的流量. 如果不满足输出NO. 根据周源的<一种简易的方法求解流量有上下界的网络中网络流问题> 无源汇上下界网络流的做法是: 设边u->v的下界是B(u,v),上界是C(u,v). 设M(i)为对于i结点的流入i的下界总和-流出i的下界总

【HDU 4940】Destroy Transportation system(数据水/无源无汇带上下界可行流)

Description Tom is a commander, his task is destroying his enemy’s transportation system. Let’s represent his enemy’s transportation system as a simple directed graph G with n nodes and m edges. Each node is a city and each directed edge is a directe

ZOJ2314 Reactor Cooling(无源汇流量有上下界网络的可行流)

题目大概说一个核反应堆的冷却系统有n个结点,有m条单向的管子连接它们,管子内流量有上下界的要求,问能否使液体在整个系统中循环流动. 本质上就是求一个无源汇流量有上下界的容量网络的可行流,因为无源汇的容量网络上各个顶点都满足流量平衡条件,即所有点的∑流入流量=∑流出流量,可以看成里面的流是循环流动的,类似有向图欧拉回路. 而带上下界的网络可行流的求法,是根据网络流中一个流是可行流的充分必要条件——限制条件和平衡条件,去改造原网络,转化成不带下界的容量网络来求解的.数学模型那些证明之类的不难理解,见

ZOJ 2314 Reactor Cooling(无源汇有上下界可行流)

题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=2314 题目大意: 给n个点,及m根pipe,每根pipe用来流躺液体的,单向的,每时每刻每根pipe流进来的物质要等于流出去的物质,要使得m条pipe组成一个循环体,里面流躺物质. 并且满足每根pipe一定的流量限制,范围为[Li,Ri].即要满足每时刻流进来的不能超过Ri(最大流问题),同时最小不能低于Li. 解题思路: 转自:https://www.cnbl

BZOJ 2406 二分+有上下界的网络流判定

思路: 求出每行的和  sum_row 每列的和   sum_line 二分最后的答案mid S->i  流量[sum_row[i]-mid,sum_row[i]+mid] i->n+j 流量[L,R] n+j->T 流量 [sum_line[i]-mid,sum_line[i]+mid] 套用有上下界的网络流 判一下就好了.. 这是道有上下界网络流的裸题 //By SiriusRen #include <queue> #include <cstdio> #inc

有上下界的网络流2-有源汇带上下界网络流ZOJ3229

ZOJ3229题目大意:一个屌丝给m个女神拍照,计划拍照n天,每一天屌丝可以和C个女神拍照,每天拍照数不能超过D张,而且给每个女神i拍照有数量限制[Li,Ri],对于每个女神n天的拍照总和不能少于Gi,如果有解求屌丝最多能拍多少张照,并求每天给对应女神拍多少张照:否则输出-1. 解题思路:        1.增设一源点st,汇点sd,st到第i天连一条上界为Di下界为0的边,每个女神到汇点连一条下界为Gi上界为正无穷的边,对于每一天,当天到第i个女孩连一条[Li,Ri]的边.        2.

有上下界的网络流

解决上下界网络流的一般思路: 解决这类问题的关键是如何去掉下界带来的麻烦.下面的哈工大出版的<图论及应用>里的思路. 1.网络的必要弧和构建附加网络 前一个数是下界,后一个数是上界. 下面的边是必要弧,其权值为下界.上面的边容量为上界与下界的差. 添加附加源点Y,附加汇点X (别弄错了) ,<X , Y>为的权值正无穷.对于必要弧<u , v>,添加<u,Y>,<Y,v>,容量为必要弧的容量. 删除<X, Y>,添加<T, S&

【有上下界网络流】【ZOJ】2314 Reactor Cooling

[算法]有上下界网络流-无源汇(循环流) [题解] 无源汇网络流相当于重建图后跑最大流. 循环流要求每个点(或边)的流入量和流出量相等. http://www.cnblogs.com/liu-runda/p/6262832.html http://hzwer.com/3356.html 入度>出度时(in[x]>0)时,需要流出去,所以从源向点引一条边,引诱它流出去. 入度<出度时(in[x]<0)时,需要流进来,所以从点向汇引一条边,引诱它流进来. 为何这样正确?源和汇的作用只是