网络流轻松入门

以下为自己理解,各位巨佬权当小说看好啦,若有误请指出,蟹蟹~
水流出于源,聚于汇。想象许多条错综交错的小溪从源头流出,汇聚到另一头的大海中,网络流描述的问题就是,小溪流入的宽度各不相同,换句话说就是分段限流,假设源头流入的水量是无限的,问水源一次无限的水灌入后能有多少汇聚到大海?
灵魂画手登场
在这里插入图片描述
如果要手算,肯定从最开始找,每条可能的小溪都尝试一下,最后得出最大的结果。
计算机也是如此,每条可能的小溪被官方的称作“增广路”,网络流最简单的应该就是EK算法,这种算法,就是每次深搜找增广路,加上答案,就是酱紫,然后所谓dinic算法,是用bfs找增广路,加上弧优化、炸点优化等等,就能跑的飞快,据说能跑1e6?
深入的部分就是找到增广路以后,对增广路怎么处理,这里也是最难懂的地方,其实通俗的讲就是找到每条增广路以后,有效的结果就是这条路里最窄的地方了,就是让这条路所有边都减掉最窄的地方,留下的就是残流,可以给别的增广路用到啦~但是吧,增广路的选取肯定先后的结果是不一样的,所以要给计算机一个反悔的机会呀,因为如果不给的话先被选到的地方不一定是最优的啦,所以就要建一条权值为0的反向边咯。
这里贴一发dinic板子。

#include<bits/stdc++.h>
#define fio ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define lson rt<<1, l, mid
#define rson rt<<1|1, mid+1, r
#define PI acos(-1)
#define eps 1e-5
#define rint register int
using namespace std;
typedef long long LL;
typedef pair<LL, int> pli;
typedef pair<int, int> pii;
typedef pair<double, int> pdi;
typedef pair<LL, LL> pll;
typedef pair<double, double> pdd;
typedef map<char, int> mci;
typedef map<string, int> msi;
template<class T>
void read(T &res) {
    
    
  int f = 1; res = 0;
  char c = getchar();
  while(c < '0' || c > '9') {
    
     if(c == '-') f = -1; c = getchar(); }
  while(c >= '0' && c <= '9') {
    
     res = res * 10 + c - '0'; c = getchar(); }
  res *= f;
}
const int INF = 2e9;
const int N = 1e4+4;
const int M = 2e5+5;
const LL Mod = 1e9+7;
struct xx {
    
    
  int next, to, w;
}edge[M];
//记得tot要置为1噢~方便后面计算啦,就不用初始化head数组啦
int tot = 1, head[N];
void Add(int u, int v, int w) {
    
    
  edge[++tot] = xx {
    
    head[u], v, w};
  head[u] = tot;
}
int n, m, s, t;
int dep[N];
int bfs() {
    
     //找增广路
  memset(dep, 0, sizeof dep);
  queue<int> q;
  while(!q.empty()) q.pop();
  q.push(s); dep[s] = 1;
  while(!q.empty()) {
    
    
    int u = q.front(); q.pop();
    for(int i = head[u], v; i; i = edge[i].next) {
    
    
      v = edge[i].to;
      if(edge[i].w && !dep[v]) {
    
    
        dep[v] = dep[u] + 1; q.push(v);
      }
    }
  }
  return dep[t]; //如果汇点没有被更新到就是0啦~
}
//这里就是深入的部分啦,是对每条增广路的处理
int dfs(int u, int in) {
    
     
  if(u == t) return in;
  int out = 0;
  for(int i = head[u], v; i && in; i = edge[i].next) {
    
    
    v = edge[i].to;
    if(dep[v] == dep[u] + 1 && edge[i].w) {
    
    
      //min过程就是找这条增广路的最窄部分啦
      int x = dfs(v, min(in, edge[i].w));
      in -= x; edge[i].w -= x;
      //反向边啦,i^1是访问反向边啦(tot要初始1噢)
      out += x; edge[i^1].w += x;
    }
  }
  if(!out) dep[u] = 0; //强有力的炸点优化
  return out;
}
int dinic() {
    
    
  int res = 0;
  while(bfs()) {
    
    
    res += dfs(s, INF);
  }
  return res;
}
int main() {
    
    
  scanf("%d%d%d%d", &n, &m, &s, &t);
  for(int i = 0, u, v, w; i < m; ++i) {
    
    
    scanf("%d%d%d", &u, &v, &w);
    Add(u, v, w); Add(v, u, 0);
  }
  printf("%d\n", dinic());
  return 0;
}

最后啦,推荐一下我学网络牛的地方啦~

网络流详解

【算法讲堂】【电子科技大学】【ACM】网络流入门

猜你喜欢

转载自blog.csdn.net/qq_43408978/article/details/105998365