以 HDU 4183 Pahom on Water 为例的 模板代码
#include <cstdio> #include <iostream> #include <cstdlib> #include <cstring> #include <cctype> #include <cmath> #include <algorithm> #include <vector> #include <queue> #include <stack> #include <map> using namespace std; const int inf = 0x3f; const int INF = 0x3f3f3f3f; const int maxn = 40002; struct Node { int to, flow, next; }edge[maxn * 8]; int n, m; int S, T, tot; int q[maxn]; int dis[maxn]; //记录层次 int pre[maxn], rec[maxn], head[maxn]; bool block[maxn]; inline void Add_edge(int a, int b, int c) { edge[tot].to = b; edge[tot].flow = c; edge[tot].next = head[a]; head[a] = tot++; edge[tot].to = a; edge[tot].flow = 0; edge[tot].next = head[b]; head[b] = tot++; } void Init() { tot = 0; memset(head, -1, sizeof(head)); } void BFS() { int i; int h = 0, t = 0; memset(dis, INF, sizeof(dis)); q[h] = S; dis[S] = 0; while(h <= t) { int u = q[h++]; for(int i = head[u]; i != -1; i = edge[i].next) { int v = edge[i].to; if(dis[v] == INF && edge[i].flow) { dis[v] = dis[u] + 1; q[++t] = v; } } } } int Dinic() { int i, j; int top = S, MaxFlow = 0, mins = INF; pre[S] = S; BFS(); memset(block, 0, sizeof(block)); while(dis[T] != INF) { for(i = head[top]; i != -1; i = edge[i].next) { if(edge[i].flow && dis[top]+1 == dis[edge[i].to] && !block[edge[i].to]) break; } if (i != -1) { j = edge[i].to; mins = min(mins, edge[i].flow); pre[j] = top; rec[j] = i; top = j; if(top == T) { i = -1; MaxFlow += mins; for(; top != S; top = pre[top]) { edge[rec[top]].flow -= mins; edge[rec[top]^1].flow += mins; if(!edge[rec[top]].flow) { i = top; } } if(i != -1) { i = pre[i]; mins = INF; for(j = i; j != S; j = pre[j]) { mins = min(mins, edge[rec[j]].flow); } top = i; } } } else { block[top] = true; top = pre[top]; if(block[S]) { BFS(); memset(block, 0, sizeof(block)); } } } return MaxFlow; } struct Pad { double x, y, rad; double fre; }pad[maxn]; bool cmp(Pad A, Pad B) { return A.fre < B.fre; } int main() { int test, a, b; scanf("%d", &test); while(test--) { Init(); scanf("%d", &n); for(int i = 1; i <= n; i++) { scanf("%lf %lf %lf %lf", &pad[i].fre, &pad[i].x, &pad[i].y, &pad[i].rad); } sort(pad+1, pad+n+1, cmp); for(int i = 1; i <= n; i++) { //printf("%d-->%lf %lf %lf %lf ", i, pad[i].fre, pad[i].x, pad[i].y, pad[i].rad); for(int j = i + 1; j <= n; j++) { if((pad[j].x-pad[i].x) * (pad[j].x-pad[i].x) + (pad[j].y-pad[i].y) * (pad[j].y-pad[i].y) < (pad[i].rad + pad[j].rad) * (pad[i].rad + pad[j].rad)) { Add_edge(i, j, 1); } } } S = 1; T = n; a = Dinic(); Init(); for(int i = n; i >= 1; i--) { for(int j = i - 1; j >= 1; j--) { if((pad[j].x-pad[i].x) * (pad[j].x-pad[i].x) + (pad[j].y-pad[i].y) * (pad[j].y-pad[i].y) < (pad[i].rad + pad[j].rad) * (pad[i].rad + pad[j].rad)) { Add_edge(i, j, 1); } } } S = n; T= 1; b = Dinic(); if(a>1 && b>1) puts("Game is VALID"); else puts("Game is NOT VALID"); } return 0; }
下面对Dinic算法的分析
Dinic:
Dinic算法实质是Edmonds-Karp算法的改进。回想下,在Edmonds-Karp算法中,每次利用BFS找出最短(经过的节点数最少)的增广路,然后进行增广,并修改残留网络。
如何改进?
假如我们能预知哪些路径不是最短的,那么每次寻找增广路的时候不就一定能走最短的那些吗?
如何预知?
当然就是预处理,在寻找增广路之前,我们可以从源点出发:做一次BFS,那么很容易就计算出了每个顶点到源点的距离(经过的节点数)。所以层次图就相当于是一个已经预处理好的增广路标志图。然后在层次图的基础上进行增广,直到无法找到增广路。然后在新的残留网络下重新计算出层次图,继续增广,直到某次计算层次图时,源点已经无法到达汇点,算法结束,此时便已找到了最大流。
在进一步介绍dinic算法的具体实现和优化前,必须先理解几个名词:
1顶点的层次
在残留网络中,我们把从源点到点的最短路径长度称作点的层次,记为。源点的层次为0。在下面这张残留网络中:
2层次图的概念
我们这样定义层次图:对于剩余图中的一条边,当且仅当时,边;
直观地讲,层次图是建立在剩余图(残留网络)基础之上的一张“最短路图”。从源点开始,在层次图中沿着边不管怎么走,经过的路径一定是终点在剩余图中的最短路。
3阻塞流的概念
在流量网络中存在一可行流,当该网络的层次图中不存在增广路时,我们称流函数为层次图的阻塞流。
4阻塞点的概念
当从残留网络中节点i出发,无法到达i的下一层次的节点时,已经不可能存在经过i的最短增广路了,即i已经被阻塞了,此时称节点i为阻塞点。显然寻路过程中寻找的节点必须是非阻塞点。
Dinic 算法流程如下:
1) 计算残留网络的层次图。我们定义 dis 为顶点 i 距离源 S 所经过到最少节点数,求出所有顶点的 dis 值,dis[] 值相同的顶点属于同一层,这就是网络的层次图。
2) 在层次图上进行 BFS(或用DFS) 增广,直到不存在增广路径。这时求得的增广路径上顶点是分层的,路径上不可能存在两个顶点属于同一层,即 dis[i]== dis[j]
(i ≠ j )。同时,求得层次图后,我们可以在层次图上进行多次增广。
3) 重复 1 和 2。直到层次图中源点已经无法到达汇点,即已不存在增广路。
优化:
多路增广:注意到,每次BFS(或DFS)完成后,会找到路径中容量最小的一条边。在这条边之前的路径的容量是大于等于这条边的容量的。那么从这条边之前的点,可能引发出别的增广路径。
比如说 S -> b -> c -> d -> T 是一条增广路径,容量最小的边是 b -> c。
可能存在一条 S -> b -> e -> f -> g -> T 这样的增广路径。
这样的话,在找到第一条增广路径后,只需要回溯到 b 点,就可以继续找下去了。
这样做的好处是,避免了找到一条路径就从头开始寻找另外一条的开销。也就是再次从 S 寻找到 b 的开销。
同时,在同一次的BFS( 或DFS) 中。如果判断出一个点是阻塞点,就将这个点在层次图中抹去。
文章最开始即为Dinic算法的实现.
转载自YB