割
网络流中的割被定义为一种点集的划分方式,源点 $s\in S$ ,汇点 $t\in T$
割 $(S,T)$ 的容量为 $c(S,T)$ 表示所有从 $S$ 到 $T$ 的边的容量之和
最小割问题就是求一个割 $(S,T)$ 使得这样划分的 $c(S,T)$ 最小
最大流最小割
一个网格 $G=(V,E)$ 的最大流就是这个图的最小割
得到一种最小割的划分方式:
void mincut(int u){
vis[u] = 1;
for (int i = h[u]; i; i = e[i].ne){
int v = e[i].v;
if (!vis[v] && e[i].c)
mincut(v);
}
}
求最小割的最少边数:将求了最大流后的图重新建立,把第一遍 Dinic 中剩余容量为 $0$ 的正向边的边权设为 $1$,其他正向边设为无穷大,反向边都设为零,因为只有流满的边才是最小割中的边
再对这个图做一遍 Dinic 操作,这一次得到的最大流就是最小割所对应的最少边数
for (int i = 0; i < m ; i ++){
if (e[i * 2 + 2].c){
add(a[i], b[i], 1e9);
add(b[i], a[i], 0);
}else{
add(a[i], b[i], 1);
add(b[i], a[i], 0);
}
}
费用流
给定的网络种每条边除了有流量限制还存在一个每个单位流量的费用 $w$,当边 $e$ 的流量为 $f$ 时,需要花费 $f\times w$ 的费用
利用 SPFA 替换掉最大流中寻找增广路的过程,可以正确计算出无负环网络的最小费用最大流
**链式前向星建边时,也要对反向边设置退费 $**-w$
for (int i = 0; i < m; i ++){
int u, v, c, w;
cin >> u >> v >> c >> w;
add(u, v, c, w);
add(v, u, 0, -w);//反向边
}
SPFA的实现
bool spfa(){
memset(dis, 0x3f, sizeof dis);
memset(mf, 0, sizeof mf);
memset(vis, 0, sizeof vis);
queue<int> q;
q.push(s);
vis[s] = 1;
dis[s] = 0;
mf[s] = 1e9;
while (q.size()){
auto u = q.front();
q.pop();
vis[u] = 0;
for (int i = h[u]; i; i = e[i].ne){
int v = e[i].v;
LL c = e[i].c, w = e[i].w;
if (dis[v] > dis[u] + w && c){//计算费用最短路
dis[v] = dis[u] + w;
mf[v] = min(mf[u], c);
pre[v] = i;//ek算法依赖的前驱边数组
if (!vis[v]){
q.push(v);
vis[v] = 1;
}
}
}
}
return mf[t] > 0;//判断是否存在增广路
}
以 EK 算法为例
pair<int, int> ek(){
LL flow = 0;
LL cost = 0;
while (spfa()){
int v = t;
while (v != s){
int i = pre[v];
e[i].c -= mf[t];
e[i ^ 1].c += mf[t];
v = e[i ^ 1].v;
}
flow += mf[t];
cost += mf[t] * dis[t];//计算当前残留网中的最小费用最大流
}
return {flow, cost};
}
时间复杂度为 $O(nmf)$ ,其中 $f$ 为最大流,但是这是伪多项式时间的
评论区(暂无评论)