最大流笔记(一)

1. 流网络

  1. 概述:在图论中,一个流网络是指一个有向图,其中每条边都有一个容量限制并可以接受,满足每一条边的流量不会超过它的容量。一道流必须符合一个结点的进出的流量相同的限制,除非这是一个源点──只有向外的流,或是一个汇点──只有向内的流。这种流网络可以用来建模很多实际问题,如液体在管道中的流动、装配线上部件的流动、电网中电流的流动和通信网络中信息的流动等。

  2. 流网络(Flow Network):一个有向图\(G=(V,E)\),图中每条边\((u,b) \in E\)有一个非负的容量值\(c(u,v) \geqslant 0\),称为容量函数。规定两个特殊的结点,源点\(s\)和汇点\(t\)\((G,c,s,t)\)就称为一个流网络

  3. 流(Flow):一个流网络中的流是一个实值函数\(f:V \times V \rightarrow \mathbb{R}\),满足以下三条性质:

    • 容量限制(Capacity Constraints)\(f(u,v) \leqslant c(u,v)\),即一条边上的流量不能超过这条边上的容量。
    • 斜对称(Skew Symmetry)\(f(u,v)=-f(v,u)\),由\(u\)\(v\)的净流必须是由\(v\)\(u\)的净流的相反数。
    • 流守恒(Flow Conservation)\(\forall v \in V/\{s,t\},\ \sum_{(u,v) \in E}{f(u,v)} = \sum_{(v,z) \in E}{f(v,z)}\),即对于除源点和汇点外的每个结点,流入的流量等于流出的流量。
  4. 一个标注了每条边的容量和流量的流网络(横线下为容量,横线上为流量)。

2. 最大流 Dinic算法

  1. 最大流问题:我们希望在不违反任何容量限制的情况下,使从源点出发的流的总量最大或者流入汇点的流的总量最大。我们想知道这个最大值是多少,这就是最大流问题。

  2. Ford-Fulkerson方法:Ford-Fulkerson方法可以用来解决最大流问题。之所以称其为“方法”而不是“算法”,是因为它包含了几种运行时间各不相同的具体实现,Dinic算法就是其中的一种实现。Ford-Fulkerson方法的基本思想是:开始时网络中初始的流值为\(0\),在每一次迭代中,在“残余网络”中寻找一条“增广路径”,沿增广路径增加流量,直到残余网络中不存在增广路径为止。最大流最小割定理保证了在算法结束时,该算法将获得一个最大流。

  3. 残余网络(Residual Network):一条边的剩余容量定义为\(c_f(u,v)=c(u,v)-f(u,v)\),由此可以构造出剩余网络\(G_f(V,E_f)\),它显示了网络中剩余的可用容量。形式化地定义,对于一个流网络\((G,c,s,t)\),它的残余网络为\((G_f,c_f,s,t)\),其中\(G_f=(V,E_f)\)\(E_f=\{(u,v) \in V \times V: c_f(u,v) > 0\}\)\(c_f(u,v)=c(u,v)-f(u,v)\)

  4. 上文的流网络的残余网络,图中标注出了每条边的剩余容量。(注意一些原来没有容量的边在残余网络中有了容量)

  5. 增广路径(augmenting path):增广路径是一条路径\((u_1,u_2,\cdots,u_k)\),其中\(u_1=s\)\(u_k=t\)\(c_f(u_i,u_{i+1})>0,(1 \leqslant 1 < k)\)。增广路径表示沿这条路径传送更多的流量是可能的。当残余网络中没有增广路径时得到最大流。

  6. 割(Cut):一个流网络\((G,c,s,t),G=(V,E)\)的割\(C=(S,T)\)是顶点集合\(V\)的一个划分,满足\(s \in S\)\(t \in T\)。割\((S,T)\)的容量是\(c(S,T)=\sum_{u \in S, v \in T}{c(u,v)}\)

  7. 最大流最小割定理:最大流 = 最小割。(限制流量的瓶颈)

  8. Dinic算法:每次对残余网络BFS标注层次图,即用数组\(level[i]\)记录\(i\)点在BFS树中的深度。然后严格按照层次顺序不断DFS寻找增广路,即增广路上结点的层次编号严格递增。如果残余网络中已经不存在到增广路了就重新标BFS注层次图,再次不断DFS增广,直到源点与汇点不再连通。累计每次增广得到的流量就得到最大流。

Code

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int N = 1e3 + 10;
const int M = 2e3 + 10;

struct Edge
{
	int to, c, next;
	Edge() {}
	Edge(int to, int c, int next) : to(to), c(c), next(next) {}
} edge[M];
int adj[N], tot;

void init()
{
	memset(adj, -1, sizeof(adj));
	tot = 0;
}

void add(int u, int v, int c)
{
	edge[tot] = Edge(v, c, adj[u]);
	adj[u] = tot++;
	edge[tot] = Edge(u, 0, adj[v]);
	adj[v] = tot++;
}

int level[N];
queue<int> q;
bool bfs(int s, int t)
{
	while (!q.empty()) q.pop();
	memset(level, -1, sizeof(level));
	level[s] = 0; q.push(s);
	while (!q.empty())
	{
		int u = q.front(); q.pop();
		for (int i = adj[u]; i != -1; i = edge[i].next)
		{
			Edge &e = edge[i];
			if (e.c && level[e.to] == -1)
			{
				level[e.to] = level[u] + 1;
				if (e.to == t) return true;
				q.push(e.to);
			}
		}
	}
	return false;
}

int cur[N];
int dfs(int u, int t, int flow)
{
	if (u == t) return flow;
	for (int &i = cur[u]; i != -1; i = edge[i].next)
	{
		Edge &e = edge[i];
		if (e.c && level[e.to] > level[u])
		{
			int f = dfs(e.to, t, min(flow, e.c));
			if (f)
			{
				e.c -= f;
				edge[i ^ 1].c += f;
				return f;
			}
		}
	}
	return 0;
}

int dinic(int s, int t)
{
	int flow = 0;
	while (bfs(s, t))
	{
		memcpy(cur, adj, sizeof(adj));
        int f;
		while (f = dfs(s, t, INF)) flow += f;
	}
	return flow;
}

int main()
{
    int n, m;
    scanf("%d%d", &n, &m);
    init();
    while (m--)
    {
        int u, v, c;
        scanf("%d%d%d", &u, &v, &c);
        add(u, v, c);
    }
	int s, t;
    scanf("%d%d", &s, &t);
    int ans = dinic(s, t);
    printf("%d\n", ans);
	return 0;
}

Input

第一行给出结点数\(n\)和边数\(m\),接下来的\(m\)行,每行给出两个三个整数\(u\)\(v\)\(c\),表示从\(u\)\(v\)有一条容量为\(c\)的边。最后一行给出源点\(s\)和汇点\(t\)

Output

输出一个整数,表示最大流。

Sample Input

6 9
0 1 10
0 2 10
1 2 2
1 3 4
1 4 8
2 4 9
3 5 10
4 3 6
4 5 10
0 5

Sample Output

19
posted @ 2017-08-20 16:50  达达Mr_X  阅读(280)  评论(0编辑  收藏  举报