今天来讲讲dinic算法。
在学习dinic算法之前,我觉得我们可以先学学链式前向星的数据结构。在做题的过程中,很多题目卡内存,再用邻接矩阵不太现实。
链式前向星
链式前向星2
之前一直不懂链式前向星的遍历,那个head数组看不懂,所以在学习的时候,最好拿出笔把那个数组给画出来,然后在遍历的时候一一找遍历时候对应的边,就会发现他遍历时候的一个顺序。
好了 然后重点。我们dinic算法。
dinic算法
dinic算法呢是一个叫Dinitz的人提出的,而这种算法和ek算法的不同在于,他发现了一种新的找增广路的新的办法。即先对图进行分层,找增广路的时候就能沿着分好层的图一步一步往前走。
下面是这个算法的一个做法。
- BFS ——> 遍历一遍每一个点,对图进行分层。。
- DFS ——> 进行增广,找一条链。 然后在dfs里面我们对起点的那几条边进行一个遍历,就可以进行多次增广。
- dinic ——> 将BFS 和DFS 组合在一起。
typedef struct Edge{
int u, v, c, next;
}edge;
void adde(int u, int v, int c){
e[hcnt].u = u; e[hcnt].v = v;e[hcnt].c = c;
e[hcnt].next = head[u]; head[u] = hcnt ++;
e[hcnt].u = v; e[hcnt].v = u;e[hcnt].c = 0;
e[hcnt].next = head[v]; head[v] = hcnt ++;
}
//链式向前星的一些操作。 增边的时候可以把反边也加一下。
//这里用了一个小技巧 我们把一个偶数一个奇数进行组合,比如 01 23 34 这样把一个边与1进行异或的时候,就是另一条边。
bool bfs(){
queue <int> q;
memset(dep, -1, sizeof (dep));//每次进来的时候一定要把图层刷新一遍
q.push(S); dep[S] = 0;
while (!q.empty()){
int h = q.front(); q.pop();
if (h == T) return true;
for (int i=head[h]; ~i; i=e[i].next){
int v = e[i].v;
if (dep[v] != -1 || e[i].c <= 0) continue;
dep[v] = dep[h] + 1;
q.push(v);
}
}
return false;
}
//BFS进行分层操作
int dfs(int s, int mw){
if (s == T) return mw; //遇到汇点我们就返回
if (mw == 0) return mw;//如果到这里的流是0了 也没有必要走下去了、
int flow = 0;
for (int i=head[s]/*(cur[s])*/; ~i; i = e[i].next){
//cur[s] = i;
int v = e[i].v;
if (dep[v] == dep[s] + 1 && e[i].c > 0){
flow = dfs(v, min(mw, e[i].c));
if (flow > 0){
e[i].c -= flow;
e[i^1].c += flow;
return flow;
}
}
}
return 0;
}
int dinic(){
int ans = 0;
while (bfs()){
// for (int i=0; i<N; i++) cur[i] = head[i];
int v = dfs(1, INF);
ans += v;
}
return ans;
}
所以dinic算法所要做的事情就是一个dfs 一个bfs。。
然后dinic算法有两个优化。。
1.当前弧优化
2.当我们bfs第一次遇到汇点的时候,我们就可以直接返回true。
弧优化的代码我已经写在了上面的代码里面,而这大概是一个什么东西呢。。
就是刚开始的算法里面,我们在找增广路的时候,我们依然还在考虑已经增广过得路,即已经走过了的不能再走的了那条空路。
而当前弧优化,就是开个数组把这个东西存一存,每次我们从当前弧开始走就行了。
至于第二个优化,貌似有论文证明了,我也就没有操心了= =、 能力有限。