1. 程式人生 > >【圖論】網絡流總結

【圖論】網絡流總結

hdu 3338 -m ini post 平衡 題目 esp urn data-

【圖論】網絡流總結

最大流部分

網絡流題目的關鍵:看出是網絡流而且確定正確的模型

最大流算法:用來解決從源點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 有源匯上下界最大流

【圖論】網絡流總結