在阅读本文前,建议先自学最大流的Ek算法。ios
Ek的核心是执行bfs,一旦找到增广路就停下来进行增广。换言之,执行一遍BFS执行一遍DFS,这使得效率大大下降。因而咱们能够考虑优化。算法
在一次BFS中,找到的增广路可能不止一条,这时咱们能够本着“尽可能少进行BFS”的想法,在一次bfs后把全部能增广的路径所有增广。
具体怎么作呢?
仍然是:
while(bfs(源点,汇点)) dfs();数组
每次bfs标记出每一个点的“深度”,也就是距离源点的长度。咱们将获得的新图称做分层图。接下来咱们在分层图上进行增广,把能增广的路径所有增广。
其它部分和Ek大致相同。优化
实际上是一个很强的优化spa
每次增广一条路后能够看作“榨干”了这条路,既然榨干了就没有再增广的可能了。但若是每次都扫描这些“枯萎的”边是很浪费时间的。那咱们就记录一下“榨取”到那条边了,而后下一次直接从这条边开始增广,就能够节省大量的时间。这就是当前弧优化code
具体怎么实现呢,先把链式前向星的head数组复制一份,存进cur数组,而后在cur数组中每次记录“榨取”到哪条边了。blog
//by floatiy #include<iostream> #include<cstdio> #include<algorithm> #include<queue> using namespace std; const int MAXN = 10000 + 5; const int MAXM = 100000 + 5; const int INF = 1e9; int n,m; int s,t;//源点 汇点 int maxflow;//答案 struct Edge { int next; int to,flow; } l[MAXM << 1]; int head[MAXN],cnt = 1; int deep[MAXN],cur[MAXN];//deep记录bfs分层图每一个点到源点的距离 queue <int> q; inline void add(int x,int y,int z) { cnt++; l[cnt].next = head[x]; l[cnt].to = y; l[cnt].flow = z; head[x] = cnt; return; } int min(int x,int y) { return x < y ? x : y; } int dfs(int now,int t,int lim) {//分别是当前点,汇点,当前边上最小的流量 if(!lim || now == t) return lim;//终止条件 // cout<<"DEBUG: DFS HAS BEEN RUN!"<<endl; int flow = 0; int f; for(int i = cur[now]; i; i = l[i].next) {//注意!当前弧优化 cur[now] = i;//记录一下榨取到哪里了 if(deep[l[i].to] == deep[now] + 1 //谁叫你是分层图 && (f = dfs(l[i].to,t,min(lim,l[i].flow)))) {//若是还能找到增广路 flow += f; lim -= f; l[i].flow -= f; l[i ^ 1].flow += f;//记得处理反向边 if(!lim) break;//没有残量就意味着不存在增广路 } } return flow; } bool bfs(int s,int t) { for(int i = 1; i <= n; i++) { cur[i] = head[i];//拷贝一份head,毕竟咱们还要用head deep[i] = 0x7f7f7f7f; } while(!q.empty()) q.pop();//清空队列 其实没有必要了 deep[s] = 0; q.push(s); while(!q.empty()) { int tmp = q.front(); q.pop(); for(int i = head[tmp]; i; i = l[i].next) { if(deep[l[i].to] > INF && l[i].flow) {//有流量就增广 //deep我赋的初值是0x7f7f7f7f 大于 INF = 1e9) deep[l[i].to] = deep[tmp] + 1; q.push(l[i].to); } } } if(deep[t] < INF) return true; else return false; } void dinic(int s,int t) { while(bfs(s,t)) { maxflow += dfs(s,t,INF); // cout<<"DEBUG: BFS HAS BEEN RUN!"<<endl; } } int main() { cin>>n>>m;//点数边数 cin>>s>>t; int x,y,z; for(int i = 1; i <= m; i++) { scanf("%d%d%d",&x,&y,&z); add(x,y,z); add(y,x,0); } // cout<<"DEBUG: ADD FININSHED!"<<endl; dinic(s,t); printf("%d",maxflow); return 0; }