上下界网络流-无源汇可行流与有源汇最大流

上下界网络流

2021.9.3

无源汇上下界可行流

之前的最大流讨论一般为有源无下届情况,那么无源汇有上下界可行流应如何求解?

首先要做的是消除下边界,应如何做?在有下届情形下,流网络中的任意一条边的可行流应满足 f m i n ( u , v ) ≤ c ( u , v ) ≤ f m a x ( u , v ) fmin(u, v) \le c(u, v) \le fmax(u, v) fmin(u,v)c(u,v)fmax(u,v),由此可变换公式得 0 ≤ c ( u , v ) − f m i n ( u , v ) ≤ f m a x ( u , v ) − f m i n ( u , v ) 0 \le c(u, v) - fmin(u, v) \le fmax(u, v) - fmin(u, v) 0c(u,v)fmin(u,v)fmax(u,v)fmin(u,v),此时,构建的流网络中边的下届均为 0 0 0.

但是,此时引入了新的问题:流量是否守恒?绝大部分情况下,不守恒。新网络中,由上方公式构建的可行流,排除下届后守恒,但是有如下情形:设当前点为 i i i,入边为 j j j,出边为 k k k,若 ∑ j j ⊂ e d g e [ i ] f m i n ( j ) ! = ∑ k k ⊂ e d g e [ i ] f m i n ( k ) \sum_{j}^{j \subset edge[i]}{fmin(j)} != \sum_{k}^{k \subset edge[i]}{fmin(k)} jjedge[i]fmin(j)!=kkedge[i]fmin(k) ,则此时总流量不守恒。

如何解决?已知本题类型为无源汇,因此每个结点,都可理解为一个源头。但不能直接从结点加入流量,若直接从结点算作无限流量,则无法保证整条路中的流量守恒。

此时可加入一个虚拟源点 S S S和一个虚拟汇点 T T T,当一个结点i的入边下届小于出边下届,便从源点 S S S向i连接一条最大流量为 ∑ j j ⊂ e d g e [ i ] f m i n ( j ) − ∑ k k ⊂ e d g e [ i ] f m i n ( k ) \sum_{j}^{j \subset edge[i]}{fmin(j)} - \sum_{k}^{k \subset edge[i]}{fmin(k)} jjedge[i]fmin(j)kkedge[i]fmin(k) 的边,当 i i i的入边下届大于出边下届,便从 i i i T T T连接一条最大流量为 ∑ k k ⊂ e d g e [ i ] f m i n ( k ) − ∑ j j ⊂ e d g e [ i ] f m i n ( j ) \sum_{k}^{k \subset edge[i]}{fmin(k)} - \sum_{j}^{j \subset edge[i]}{fmin(j)} kkedge[i]fmin(k)jjedge[i]fmin(j) 的边,由此保证了整个网络的流量守恒 − - 流量中每条边的流量之和等于每个结点产生的流量,此时可把每个点产生的流量转移到我们自行添加的虚拟源点之上。

代码

cin >> n >> m;
S = 0, T = n + 1;
memset(head, -1, sizeof head);
for (int i = 0; i < m; i++) {
	int a, b, c, d;
	cin >> a >> b >> c >> d;
	add(a, b, c, d);
	A[a] -= c, A[b] += c;
}
int tot = 0;
for (int i = 1; i <= n; i++) {
	if (A[i] > 0)add(S, i, 0, A[i]), tot += A[i];
	else if (A[i] < 0)add(i, T, 0, -A[i]);
}

//上述add函数为
void add(int a, int b, int c, int d) {
	to[idx] = b, wei[idx] = d - c, l[idx] = c, nexte[idx] = head[a], head[a] = idx++;
	to[idx] = a, wei[idx] = 0, nexte[idx] = head[b], head[b] = idx++;
}

这时候问题就转化成了与https://blog.csdn.net/Tinberlake/article/details/119751664?spm=1001.2014.3001.5501上相同的有源求最大流问题,此时每个边在原图中的流量就是新图中计算的流量加上其下届流量 f m i n ( u , v ) + c ′ ( u , v ) fmin(u, v) + c'(u, v) fmin(u,v)+c(u,v)

有源汇上下界最大流

此时的网络中有源点,每个结点只能接受从源点汇来的流量,无法同无源汇上下界可行流一样通过填补每个结点的流量进行计算。

此时如何求得网络的最大流?此时,先将题目中的源点与汇点定义为 s s s t t t,另新建两虚拟结点 S S S T T T,建立一条 t t t s s s可行流量为无穷的边,此时先将问题转化成了无源汇上下界可行流。

那么,为何从汇点到源点建立一条流量为正无穷的边?保证流量守恒

若要求最大流,则从 s s s t t t的流量一定为正, 0 ≤ c ( s , t ) 0 \le c(s, t) 0c(s,t) ,若要保证新图与旧图流量守恒的一致,则需要在 t t t s s s之间建立一条流量为无穷的边。

那么,对于新图中流量若小于从虚拟源点流出的最大流,则可证明没有一条可行流满足从 s s s t t t,此时存在边不满足下届。

当新图中的流量不小于虚拟源点流出的最大流,此时截断从 t t t s s s流量为无穷的边,并将此边的逆边流量记录为 r e s res res。将源汇点更改为 s s s t t t,那么,新图中 s s s t t t的残留网络的最大流加上 r e s res res即为有源汇上下界最大流的流量。

为何?新图中无源汇的可行流构建了一个可行流,这个可行流最大时,代表所有结点的流量均守恒,此时的残留网络,若去除 t t t s s s之间流量为无穷的结点,则代表此时除去这两点外,其余所有点的流量均守恒。这两点作为源点与汇点,不必守恒,因此此时计算去边后残留网络的最大流,可以保证整个网络的流量是守恒的。又因为,新图中原本 s s s t t t的不守恒流量的大小,就是被截断边的逆边的流量,因此 s s s t t t的最大可行流就是 r e s res res加新图中 s s s t t t的残留网络的最大流。

代码

cin >> n >> m >> s >> t;
S = 0, T = n + 1;
memset(head, -1, sizeof head);
while(m--){
    int a, b, c, d;
    cin >> a >> b >> c >> d;
    add(a, b, d - c);
    A[a] -= c, A[b] += c;
}
int all = 0;
for(int i = 1; i <= n; i++){
    if(A[i] > 0) add(S, i, A[i]), all += A[i];
    else if(A[i] < 0) add(i, T, -A[i]);
}
add(t, s, INF);
if(dinic() < all) cout << "No Solution\n";
else{
    int ans = wei[idx - 1];
    S = s, T = t;
    wei[idx - 1] = wei[idx - 2] = 0;
    cout << ans + dinic() << '\n';
}

//上述add函数为
void add(int a,int b, int c){
    to[idx] = b, wei[idx] = c, nexto[idx] = head[a], head[a] = idx++;
    to[idx] = a, wei[idx] = 0, nexto[idx] = head[b], head[b] = idx++;
}

有源汇上下界最小流

根据有源汇上下界最大流的相关计算,可知若要 s s s t t t是最小流,则此时 t t t s s s的可行流最大,因此只需按有源汇上下界最大流建图后,更改源汇点为 t t t s s s即可。

代码

cin >> n >> m >> s >> t;
S = 0, T = n + 1;
memset(head, -1, sizeof head);
while(m--){
    int a, b, c, d;
    cin >> a >> b >> c >> d;
    add(a, b, d - c);
    A[a] -= c, A[b] += c;
}
int all = 0;
for(int i = 1; i <= n; i++){
    if(A[i] > 0) add(S, i, A[i]), all += A[i];
    else if(A[i] < 0) add(i, T, -A[i]);
}
add(t, s, INF);
if(dinic() < all) cout << "No Solution\n";
else{
    int ans = wei[idx - 1];
    S = t, T = s;
    wei[idx - 1] = wei[idx - 2] = 0;
    cout << ans - dinic() << '\n';
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值