【图论】网络流总结

【图论】网络流总结

最大流部分

网络流题目的关键:看出是网络流并且确定正确的模型

最大流算法:用来解决从源点s到汇点t,整个网络最多能输送多少流量的题目

模板:

#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
using namespace std;

const int MAXNODE = 105 * 2;
const int MAXEDGE = 100005;

typedef int Type;
const Type INF = 0x3f3f3f3f;

struct Edge {
	int u, v;
	Type cap, flow;
	Edge() {}
	Edge(int u, int v, Type cap, Type flow) {
		this->u = u;
		this->v = v;
		this->cap = cap;
		this->flow = flow;
	}
};

struct Dinic {
	int n, m, s, t;
	Edge edges[MAXEDGE];
	int first[MAXNODE];
	int next[MAXEDGE];
	bool vis[MAXNODE];
	Type d[MAXNODE];
	int cur[MAXNODE];
	vector<int> cut;

	void init(int n) {
		this->n = n;
		memset(first, -1, sizeof(first));
		m = 0;
	}
	void add_Edge(int u, int v, Type cap) {
		edges[m] = Edge(u, v, cap, 0);
		next[m] = first[u];
		first[u] = m++;
		edges[m] = Edge(v, u, 0, 0);
		next[m] = first[v];
		first[v] = m++;
	}

	bool bfs() {
		memset(vis, false, sizeof(vis));
		queue<int> Q;
		Q.push(s);
		d[s] = 0;
		vis[s] = true;
		while (!Q.empty()) {
			int u = Q.front(); Q.pop();
			for (int i = first[u]; i != -1; i = next[i]) {
				Edge& e = edges[i];
				if (!vis[e.v] && e.cap > e.flow) {
					vis[e.v] = true;
					d[e.v] = d[u] + 1;
					Q.push(e.v);
				}
			}
		}
		return vis[t];
	}

	Type dfs(int u, Type a) {
		if (u == t || a == 0) return a;
		Type flow = 0, f;
		for (int &i = cur[u]; i != -1; i = next[i]) {
			Edge& e = edges[i];
			if (d[u] + 1 == d[e.v] && (f = dfs(e.v, min(a, e.cap - e.flow))) > 0) {
				e.flow += f;
				edges[i^1].flow -= f;
				flow += f;
				a -= f;
				if (a == 0) break;
			}
		}
		return flow;
	}

	Type Maxflow(int s, int t) {
		this->s = s; this->t = t;
		Type flow = 0;
		while (bfs()) {
			for (int i = 0; i < n; i++)
				cur[i] = first[i];
			flow += dfs(s, INF);
		}
		return flow;
	}

	void MinCut() {
		cut.clear();
		for (int i = 0; i < m; i += 2) {
			if (vis[edges[i].u] && !vis[edges[i].v])
				cut.push_back(i);
		}
	}
} gao;

边有容量:有向边的话直接添加就可以了,如果是无向边,可以把反向边的容量定成和正向边容量一样即可

点有容量:可以进行拆点,一个点拆成i和i',i作为入点,i'作为出点,然后i和i'之间连一条边,容量就是点的容量,然后流入i点的连向入点,流出的,从出点连出去

有一类题目是这样:先二分,然后根据二分值建图,利用最大流算法去判断,这和之前很多图论的题目都很类似,利用二分出来的值筛去一些边,然后判断就可以找出最大最小化的答案

有一类题目要进行模型的转化,这类题目就比较看智商了,还有经验

最小割

最大流还有一个很重要的应用,就是求最小割
以下是一些定理,其实这些和二分图匹配里面的有点相似:
最小割 = 最大流
最大点权覆盖集 = 最小割
最小点权独立集 = 总权值 - 最大点权覆盖集

最小割的定义:把网络划分成两个集合,s,t使得在s集合和在t集合中的任意两点互相不相连,去掉的边就是割边,而这些割边代价总和就是割,最小割就是代价最小的划分方法

一类最小割的题目还是挺明显,如果是要把存在点划分两个集合,求最小代价之类的,就很明显是最小割了

然后最小割有一些挺经典的模型:
1、平面图求最小割:

这个做法就是,把平面每一部分面积当成点,然后相邻块连边,然后增设源点s和汇点t,分别在原来的入口出口的另一对角线上,和相应两部分边连边,这时候,每一个最小割,其实就是一个s到t的路径,那么求最短路就是最小割了

2、最小权闭合

这个做法是源点s连向正权值,负权值连向汇点t,之间关系连边容量INF,求出最小割之后,这个最小割就是最少损失,然后总权值 - 最小割得到的就是最大权闭合图的权值

关于最小割输出路径

根据题意,如果要s集合尽量多,就从t集合dfs,反之则从s集合dfs,不经过没有容量的边即可

费用流部分:

这个就是网络有边权,代表每一份流量的代价,在求最大流的基础上,求出输送这些流量的最小代价

费用流的建模和最大流基本差不多,就多一个边权

费用流模板:

#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
#include <algorithm>
using namespace std;

const int MAXNODE = 105;
const int MAXEDGE = 5005 * 4;
typedef int Type;
const Type INF = 0x3f3f3f3f;

struct Edge {
	int u, v;
	Type cap, flow, cost;
	Edge() {}
	Edge(int u, int v, Type cap, Type flow, Type cost) {
		this->u = u;
		this->v = v;
		this->cap = cap;
		this->flow = flow;
		this->cost = cost;
	}
};

struct MCFC {
	int n, m, s, t;
	Edge edges[MAXEDGE];
	int first[MAXNODE];
	int next[MAXEDGE];
	int inq[MAXNODE];
	Type d[MAXNODE];
	int p[MAXNODE];
	Type a[MAXNODE];

	void init(int n) {
		this->n = n;
		memset(first, -1, sizeof(first));
		m = 0;
	}

	void add_Edge(int u, int v, Type cap, Type cost) {
		edges[m] = Edge(u, v, cap, 0, cost);
		next[m] = first[u];
		first[u] = m++;
		edges[m] = Edge(v, u, 0, 0, -cost);
		next[m] = first[v];
		first[v] = m++;
	}

	bool bellmanford(int s, int t, Type &flow, Type &cost) {

		for (int i = 0; i < n; i++) d[i] = INF;
		memset(inq, false, sizeof(inq));
		d[s] = 0; inq[s] = true; p[s] = s; a[s] = INF;
		queue<int> Q;
		Q.push(s);
		while (!Q.empty()) {
			int u = Q.front(); Q.pop();
			inq[u] = false;
			for (int i = first[u]; i != -1; i = next[i]) {
				Edge& e = edges[i];
				if (e.cap > e.flow && d[e.v] > d[u] + e.cost) {
					d[e.v] = d[u] + e.cost;
					p[e.v] = i;
					a[e.v] = min(a[u], e.cap - e.flow);
					if (!inq[e.v]) {Q.push(e.v); inq[e.v] = true;}
				}
			}
		}
		if (d[t] == INF) return false;
		flow += a[t];
		cost += d[t] * a[t];
		int u = t;
		while (u != s) {
			edges[p[u]].flow += a[t];
			edges[p[u]^1].flow -= a[t];
			u = edges[p[u]].u;
		}
		return true;
	}

	Type Mincost(int s, int t) {
		Type flow = 0, cost = 0;
		while (bellmanford(s, t, flow, cost));
		return cost;
	}
} gao;

一类K覆盖问题:

这类问题一般表现为,一个区间或者一些点,每个可以最多被覆盖k次,然后有一些特殊的边可以走,但是只能走一次,这时候要求k覆盖后的最大权值

其实就是费用流,建边把特殊边建起来,对应相应费用,然后其他边直接相连,费用为0,注意由于是要求最大代价,而算法是求最小费用,其实和KM匹配求最小一样的思路,把边权弄成负数,跑一下即可

网络流的一些特殊问题

上下界网络流:

无源无汇有上下界最大流:

这个要根据流量平衡来搞,建图先把边容量定成上限up - 下限down,然后每一个点,记录下流入流量和流出流量,然后设一个超级源s,超级汇t,s连接流量正的点,流量负的点连向t,然后跑最大流,跑完之后如果从s流出的流量都是满流,就是有解,每个边的真实流量就为当前边流量,加上原来的下限

有源有汇有上下界最大流:
建图方法一致,不过要多连上一条t->s容量为INF的边,这样跑一下最大流,t->s的流量就是答案

有源有汇有上下界最小流:
也是一样,不过t->s先不连,先求一次最大流,然后在连t->s,在做一次最大流把残余流量充分利用,然后t->s的流量就是答案

分层网络流:

这类题,以时间为单位,这样对于每个时间点就要建一层结点,时间上限不是很大的话,就可以每多一个时间点,就多一层结点,在原来的图上继续增广

混合图找欧拉回路:

欧拉回路入度等于出度,然后无向图先任意定向,然后记录每个点的度数和,度数和 / 2就是需要调整的边数,然后把源点连向正的,负的连向汇点,然后中间的边就是连无向边,因为只有无向边可以调整,然后跑一下最大流即可

增加哪些边会使得最大流增加:

这类问题其实就是对于满流的边,如果左边源点到他和他到汇点,能有一个残量网络,这条边就是可以增加的,利用两个dfs,分别从源点汇点出发即可

最大密度子图:

先要记录下每个结点的度数
利用二分搜索来搜索答案g,然后根据这个建图判断,判断的方式为:
源点与原图中每一个点连一条容量为m的边。原图中每一个点与汇点连一条容量为m+2*g-度数的边,再将原图中的无向边拆成两条有向边,容量都设为1.然后对此图求最大流,最后将(n*m-maxflow)/2 与0比较大小,如果它大于0,l = g,否则r = g

POJ上的题目:

POJ 1698 Alice's Chance 最大流+拆点
POJ 2112 Optimal Milking 二分+最大流
POJ 2455 Secret Milking Machine 二分+最大流
POJ 1149 PIGS 最大流(建模是关键)
POJ 2135 Farm Tour 费用流
POJ 2516 Minimum Cost 费用流(注意每种互不影响物品分开讨论)
POJ 3281 Dining 最大流+建模
POJ 3469 Dual Core CPU 最小割
POJ 3680 Intervals k覆盖问题
POJ 3762 The Bonus Salary! k覆盖问题
POJ 2987 Firing 最大权闭合
POJ 1637 Sightseeing tour 混合图欧拉回路
POJ 3422 Kaka's Matrix Travels 转化为k覆盖问题
POJ 3189 Steady Cow Assignment 这种是一类问题(二分差值,枚举下界,利用最大流去判断)
POJ 3204 Ikki's Story I - Road Reconstruction 增加哪些边会使得最大流增加

HDU上的题目:

HDU 1532 Drainage Ditches 最大流模板题
HDU 3081 Marriage Match II 二分+最大流
HDU 3277 Marriage Match III 二分+最大流+拆点
HDU 3416 Marriage Match IV 最短路+最大流
HDU 3376 Matrix Again 拆点+费用流
HDU 3313 Key Vertex 在最短路基础上dfs
HDU 3338 Kakuro Extension 最大流+行列模型
HDU 1565 方格取数(1) 最大点权独立集
HDU 1569 方格取数(2) 同上
HDU 3035 War 平面图求最小割
HDU 3046 Pleasant sheep and big big wolf 最小割
HDU 3251 Being a Hero 最小割
HDU 1733 Escape 分层网络流
HDU 2883 kebab 最大流+建模
HDU 2732 Leapin' Lizards 拆点+最大流
HDU 3061 Battle 最大权闭合
HDU 3157 Crazy Circuits 有源汇上下界最大流

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值