网络流

image
image
先把一些不会证的结论放着。

最大流

什么是最大流 ? 有一个图, 其中有一个源点和一个汇点, 每一条边有一定的流度最大值, 每个点有一个无线大的水桶, 源点有一个无线水的水井, 问每一条边只会经过一次水流时汇点能获得的最大水流量。

令源点为 \(s\), 汇点为 \(t\), 点数为 \(n\), 边数为 \(m\)

对于每一条从 \(s \to t\) 路径, 我们令最小值为 \(x\), 则每一条边权值都减 \(x\), 然后建一条权值一样反向边。

为什么要建反向边 ?

我们令这条边为 \(u \to v\), 则 可能有两条路径不经过 \(u \to v\) 且 经过 \(u, v\) 的路径, 不过有可能经过了这一条边, 这一条边就相当于不选原来的边。

image

如图, 我们可能会搜到红色的路径, 而更优的是绿色的路径。

Dinic

我们可以每次找一条最短的增广路径, 按照增广路径解决, 对于一个点, 先把当前的流度出来好, 如能 dfs 就先 dfs, 不急着回溯。

我们不考虑单次 dfs 的时间复杂度, 而是考虑有多少条增广路径。
每次增广, 至少会让一条边消失, 所以最多只有 \(m\) 条增广, 每条增广路径做多 \(n\) 个点, 所以每次 bfs 后跑一遍的时间复杂的最多为 \(O(nm)\), 所以时间复杂度 \(O(n^2m)\)

模板 luoguP3376

#include<bits/stdc++.h>

using namespace std;

using LL = long long;

const int N = 1e4 + 5;

struct Edge{
  int v, w, nxt;
}e[N];

int dep[N], n, m, s, t, u, v, w, head[N], tot, p[N];
long long ans, now;
queue<int>q;

void add(int u, int v, int w){
  e[tot] = {v, w, head[u]};
  head[u] = tot++;
}

LL dfs(int x, LL minx){
  LL s = 0;
  if(x == t)return minx;
  for(; ~p[x]; p[x] = e[p[x]].nxt){
    int v = e[p[x]].v, w = e[p[x]].w, id = p[x];
    if(!w || dep[v] != dep[x] + 1)continue;
    LL qp = dfs(v, min(minx, 1ll * w));
    s += qp;
    minx -= qp;
    e[id ^ 1].w += qp;
    e[id].w -= qp;
  }
  return s;
}

bool bfs(){
  for(int i = 1; i <= n; ++i){
    dep[i] = 0;
    p[i] = head[i];
  }
  dep[s] = 1;
  q.push(s);
  while(q.size()){
    auto u = q.front();
    q.pop();
    for(int i = head[u]; ~i; i = e[i].nxt){
      int v = e[i].v, w = e[i].w;
      if(!w || dep[v])continue;
      dep[v] = dep[u] + 1;
      q.push(v);
    }
  }
  return dep[t];
}

int main(){
  cin >> n >> m >> s >> t;
  for(int i = 1; i <= n; ++i){
    head[i] = -1;
  }
  for(int i = 1; i <= m; ++i){
    cin >> u >> v >> w;
    add(u, v, w);
    add(v, u, 0);
  }
  while(bfs()){
    ans += dfs(s, 1e18);
  }
  cout << ans;
  return 0;
}

最小割

什么是割, 对于一个子图 \(G(V, E)\), 若两个集合 \(S, T, S \cup T = \varnothing\), \(s \cap T = V\), 这个割的代价为 \(\sum \limits_{u, v \in E, u \in S, v \in T} r(u, v)\) (其中, r(u, v)) 表示这条割的容量。

最小割就是代价最小的割。

我们可以把这个看成删到其中的一些边, 使得 \(s, t\) 不连通。

我们可以把这个看成一个更理想的情况, 就是每次可以让一条边的流量减少 \(1\), 使得 \(s\) 无法到达 \(t\)。这样, 我们就知道 最小割 \(\ge\) 最大流。我们考虑构造, 对于每一条增广路径, 把第一条满流量的边断开。这样我们就可以构造一个最小割。
为什么 ?
我们的问题在于会不会出现一种情况一个点他原本流行另一个点, 但是由于把这条边断开了, 所以他的流量会去其他的点, 使得可以到达 \(t\)
对于在割掉的边后面的边, 显然不用考虑。
假设存在一个点 \(u\), 使得流向他的边没有满流, 假设我们存在一个点, 使得完里面注入流量还能继续到达 \(t\), 则一定是因为没有流量了(否则这是一条新的增广路径) (即来到这个点的边与假设矛盾都满了, 可以搁到流量到来的地方)。

最小割模型

我们已经学会了如何求最小割, 然我们看一些很经典的建边方法。

最大权闭合子图

我们可以解决一下问题, 有一些点, 他们有依赖关系, 形如选某些点后一定要选他的后继。每个点有一个权值, 可正可负, 对于依赖关系, 我们直接建一些边, 我们先把正权全部加起来, 然后让源点连续这些点, 边权为这个的正权, 每个负权的点连向汇点, 边权为他的绝对值。然后跑最小割。这样为什么是对的?由于存在关系的我们都连了边所以如果选等价于把右边的边断开, 不选等价于把左边的断开。

切糕模型

image

对于每个点, 我们把他分成值域个点, 我们考虑对于每个点, 如果选左边表示这个数至少大于某个值, 右边表示小于某个值, 我们可以把代价放在每条相邻的边上, 每次若割掉相当于选。

如何防止出现选左右边交替?
对于每个点, 我们连一条正无穷的反边即可。

对于每种限制, 我们可以拆成。

\(\exists c\), 若 \(x_v \le c\), 则 \(x_u \le w + c\), 这样, 我们就可以看成一个这两个点必须在同一个割的集合中, 只需要连一条正无穷的边即可。

关于网络流流量只有0/1的一些性质

如果存在一条方向变且连接两端点的变除了这条只有1条, 则如果到达一个点, 他能走的点唯一确定, 不可能离开.

posted @ 2024-12-02 21:30  liuyichen  阅读(5)  评论(0)    收藏  举报