网络流(最大流)

网络流(最大流)

给定一个网络,有源点和汇点,现在要往源点灌水,问每单位时间可以从汇点出多少水,并且每一条边有限流。

P3376【模板】网络最大流

一个几乎没用的东西:FF

思路

我们很显然会有个思路,就是每次 \(DFS\) 搜索,找到一条路径,并且是可以增广的(限流还没达到),那么就增广它,可是如果遇到一条路径走会满流,但分开走就能流出最大流的情况,所以我们需要一个反悔的机会,可以将剩余可以流的水量建一条正向边形成残余网络,已经流的建一条反向边,于是就有了一个反悔的机会。

EK

思路

\(DFS\) 改成 \(BFS\) 即可,这样可以保证每次找到的路径是最短的,但是我们在增广的时候需要把路径弄出来,所以我们用一个 \(pre\) 数组来记录上一个结点,这样就可以记录路径了。

code

#include <iostream>
#include <queue>

using namespace std;
using ll = long long;

const int MaxN = 210, MaxM = 2 * 5010;

struct Edge {
  ll v, w, nxt;
} e[MaxM];

ll h[MaxN], ans;
int n, m, s, t, cnt = 1;
bool vis[MaxN];
queue<int> q;
pair<int, int> p[MaxN];

ll G(int x, ll minx = 1e18) {
  if (x == s) {
    return minx;
  }
  ll res = G(p[x].first, min(minx, e[p[x].second].w));
  e[p[x].second ^ 1].w += res;
  e[p[x].second].w -= res;
  return res;
}

void Record(int u, int i) {
  if (vis[e[i].v] || !e[i].w) {
    return;
  }
  vis[e[i].v] = 1;
  p[e[i].v] = {u, i};
  q.push(e[i].v);
}

ll BFS() {
  for (int i = 1; i <= n; i++) {
    vis[i] = 0, p[i] = {0, 0};
  }
  queue<int>().swap(q);
  for (q.push(s), vis[s] = 1; !q.empty(); q.pop()) {
    int u = q.front();    
    if (u == t) {
      return G(t);
    }    
    for (int i = h[u]; ~i; i = e[i].nxt) {
      Record(u, i);
    }
  }
  return -1;
}

int main() {
  ios::sync_with_stdio(0), cin.tie(0);
  cin >> n >> m >> s >> t;
  fill(h + 1, h + n + 1, -1);
  for (ll i = 1, u, v, w; i <= m; i++) {
    cin >> u >> v >> w;
    e[++cnt] = {v, w, h[u]}, h[u] = cnt;     
    e[++cnt] = {u, 0, h[v]}, h[v] = cnt;
  }
  for (ll res; ~(res = BFS()); ans += res) {
  }
  cout << ans << '\n';
  return 0;
}

时间复杂度:\(O(nm^2)\)

Dinic

每次对残余网络进行分层,将非可是分层的边(树上就是横向边)删了,然后找当前图的最大流,及找到增广路径,然后增广,将当前图的最大流并入最终的最大流。

优化

  1. \(DFS\) 途中,每次将当前边,设为当前点第一条边(注意最后你要还原),因为当处理完这条边后,再处理的话,必然会再一条被增广的路径上被卡死。
  2. \(DFS\) 过程中,如果处理完一条边的答案为 \(0\),那我们标记一下,返回过程中就不走这了,同样的要还原。

code

#include <iostream>
#include <queue>

using namespace std;
using ll = long long;

const int MaxN = 210, MaxM = 2 * 5010;

struct IF {
  struct Edge {
    ll v, w, nxt;
  } e[MaxM];

  ll dis[MaxN], h[MaxN], th[MaxN], ans, n, s, t, cnt;
  bool vis[MaxN];
  queue<int> q;

  IF() {
    n = s = t = ans = 0, cnt = 1;
    fill(h, h + MaxN, -1);
    fill(dis, dis + MaxN, 0);
  }

  void Record(int u, int i) {
    if (vis[e[i].v] || !e[i].w) {
      return;
    }
    vis[e[i].v] = 1;
    dis[e[i].v] = dis[u] + 1;
    q.push(e[i].v);
  }

  bool BFS() {
    for (int i = 1; i <= n; i++) {
      vis[i] = 0, th[i] = h[i];
    }
    queue<int>().swap(q);
    for (q.push(s), vis[s] = 1, dis[s] = 1; !q.empty(); q.pop()) {
      int u = q.front();
      if (u == t) {
        continue;
      }
      for (int i = h[u]; ~i; i = e[i].nxt) {
        Record(u, i);
      }
    }
    return vis[t];
  }

  ll DFS(int x, ll f) {
    if (x == t || !f) {
      return f;
    }
    ll res = 0;
    for (int i = th[x]; ~i && f - res > 0; i = e[i].nxt) {
      th[x] = i;
      if (e[i].w && dis[x] + 1 == dis[e[i].v]) {
        ll tmp = DFS(e[i].v, min(f - res, e[i].w));
        if (!tmp) {
          dis[e[i].v] = 1e18;
        }
        res += tmp;
        e[i].w -= tmp;
        e[i ^ 1].w += tmp;
      }
    }
    return res;
  }

  void insert(int u, int v, ll w) {
    e[++cnt] = {v, w, h[u]}, h[u] = cnt;
    e[++cnt] = {u, 0, h[v]}, h[v] = cnt;
  }

  ll Solov() {
    for (; BFS(); ans += DFS(s, 1e18)) {
    }
    return ans;
  }
};

int n, m, s, t;

int main() {
  ios::sync_with_stdio(0), cin.tie(0);
  cin >> n >> m >> s >> t;
  IF ans;
  ans.n = n, ans.s = s, ans.t = t;
  for (ll i = 1, u, v, w; i <= m; i++) {
    cin >> u >> v >> w;
    ans.insert(u, v, w);
  }
  cout << ans.Solov() << '\n';
  return 0;
}

时间复杂度

最差:\(O(n^2m)\)

posted @ 2024-02-21 12:14  yabnto  阅读(5)  评论(0编辑  收藏  举报