网络流

我来透彻网络流了。因为懒得打字参考了LNOI创始人的博客qwq。说白了就是粘

网络流

网络流定义

在一个有向图上选择一个源点,一个汇点,每一条边上都有一个流量上限(以下称为容量),即经过这条边的流量不能超过这个上界,同时,除源点和汇点外,所有点的入流和出流都相等,而源点只有流出的流,汇点只有汇入的流。

这样的图叫做网络流。

网络流相关定义

  1. 源点:有\(n\)个点,有\(m\)条有向边,有一个点很特殊,只出不进,叫做源点。

  2. 汇点:另一个点也很特殊,只进不出,叫做汇点。

  3. 容量和流量:每条有向边上有两个量,容量和流量,从i到j的容量通常用\(c[i,j]\)表示,流量则通常是\(f[i,j]\)
    通常可以把这些边想象成道路,流量就是这条道路的车流量,容量就是道路可承受的最大的车流量。
    很显然的,流量\(\leq\)容量。而对于每个不是源点和汇点的点来说,可以类比的想象成没有存储功能的货物的中转站,所有“进入”他们的流量和等于所有从他本身“出去”的流量。

  4. 最大流:把源点比作工厂的话,问题就是求从工厂最大可以发出多少货物,是不至于超过道路的容量限制,也就是,最大流。

增广路算法

该方法通过寻找增广路来更新最大流,有\(EK,dinic,SAP,ISAP\)主流算法。

最常用的就是\(dinic\)

但是费用流需要用到\(EK\),所以要学会\(EK\)\(dinic\)算法;

增广路:在图中若经过一条从源点到汇点的路径后,路径上所有边的剩余容量都\(>0\)(注意是\(>\)不是\(\geq\)),那么这条路径被称为增广路。

我们通过找到增广路来求解最大流,因为只要有增广路,你的所求最大流就是不最大的,当无增广路之后,最大流就求出来了读者自证不难

dinic

#include <bits/stdc++.h>
using namespace std;

const int maxn(210), maxm(5010);

int n, m, s, t, cnt = 1, head[maxn], dep[maxn], cur[maxn];
long long ans;

struct edge{
	int x, y, z, nxt;
}e[maxm<<1];

inline void add_edge(int x, int y, int z){
	++cnt;
	e[cnt].x = x, e[cnt].y = y, e[cnt].z = z;
	e[cnt].nxt = head[x];
	head[x] = cnt;
	return;
}

bool bfs(){
	for(int i = 1; i <= n; i ++) dep[i] = 0;
	queue<int> q;
	q.push(s);
	dep[s] = 1;
	while(q.size()){
		int now = q.front();
		q.pop();
		for(int i = head[now]; i; i = e[i].nxt){
			int to = e[i].y, len = e[i].z;
			if(len and !dep[to]){
				dep[to] = dep[now] + 1;
				q.push(to);
				if(to == t) return 1;
			}
		}
	}
	return 0;
}

int dfs(int now, int num){
	if(now == t) return num;
	int res = num;
	for(int i = cur[now]; i and res; i = e[i].nxt){
		cur[now] = i;
		int to = e[i].y, len = e[i].z;
		if(len and (dep[to] == dep[now] + 1)){
			int k = dfs(to, min(res, len));
			if(!k) dep[to] = 0;
			e[i].z -= k, e[i^1].z += k, res -= k;
		}
	}
	return num-res;
}

void dinic(long long &ans){
	while(bfs()){
		for(int i = 1; i <= n; i ++) cur[i] = head[i];
		int num = 0;
		while(num = dfs(s,1<<29)) ans += num;
	}
	return;
}

signed main(){
	scanf("%d%d%d%d", &n, &m, &s, &t);
	for(int x, y, z; m; m --){
		scanf("%d%d%d", &x, &y, &z);
		add_edge(x,y,z);
		add_edge(y,x,0);
	}
	dinic(ans);
	printf("%lld", ans);
	return 0;
}

posted @ 2020-07-20 21:23  Vanyun  阅读(169)  评论(8编辑  收藏  举报