书接上回,我们继续
EK算法
EK算法即为FF算法的BFS版,所以直接上代码:
其实可以不看,因为没人用
#include <bits/stdc++.h>
#define N 2001
#define M 20002
using namespace std;
const int INF=0X3f3f3f3f;
int head[N],v[N],ver[M],edge[M],Next[M],n,m,tot;
int incf[N],pre[N],s,t,maxflow;
bool bfs(){
memset(v,0x00,sizeof(v));
queue<int> Q;
Q.push(s);
v[s] = 1;
incf[s] = INF;
while(Q.size()){
int x = Q.front();
Q.pop();
for(int i = head[x];~i;i = Next[i]){
if(edge[i]){
int y = ver[i];
if(v[y]){
continue;
}
incf[y] = min(incf[x], edge[i]);
pre[y] = i;
Q.push(y);
v[y] = 1;
if(y == t){
return 1;
}
}
}
}
return 0;
}
void ADD(int x,int y,int z){
ver[++tot] = y, edge[tot] = z, Next[tot] = head[x], head[x] = tot;
ver[++tot] = x, edge[tot] = 0, Next[tot] = head[y], head[y] = tot;
}
void update(){
int x = t, i;
while(x != s){
i = pre[x];
edge[i] -= incf[t];
edge[i ^ 1] += incf[t];
x = ver[i ^ 1];
}
maxflow += incf[t];
}
signed main(){
int x, y, z;
while(~scanf("%d %d %d %d",&n,&m,&s,&t)){
memset(head, -1, sizeof(head));
tot = -1;
maxflow = 0;
for(int i = 1;i <= m;i ++ ){
scanf("%d %d %d", &x, &y, &z);
ADD(x, y, z);
}
while(bfs()){
update();
}
printf("%d\n",maxflow);
}
return 0;
}
Dinic算法
那么就到了我们的重头戏,Dinic算法!
在之前我们提到的FF算法和EK算法中,我们发现一个问题——我们会走回头路,走着走着离汇点更远了!
为解决这个问题,Dinic算法引入一个新的维度——深度,我们只走深度比自己深的点
我们结合代码分析:
int head[N], tot = 0;
struct node{
int to, w, nxt;
}edge[M];
void add(int u, int v, int w){
edge[tot].w = w;
edge[tot].to = v;
edge[tot].nxt = head[u];
head[u] = tot ++ ;
}
加边我是用 000 为起点
int cur[N], dep[N];
bool bfs(){
memset(dep, -1, sizeof dep);
queue<int> Q;
Q.push(s);
dep[s] = 0;
cur[s] = head[s];
while(Q.size()){
int u = Q.front();
Q.pop();
for(int i = head[u];~i;i = edge[i].nxt){
int v = edge[i].to;
if(dep[v] == -1 && edge[i].w){//如果没有遍历到过
dep[v] = dep[u] + 1;//比父节点深1
cur[v] = head[v];
if(v == t) return true;
Q.push(v);
}
}
}
return false;
}
我通过 depdepdep 记录深度,并将其赋为 −1-1−1
其中 curcurcur 为当前弧优化
整个遍历通过 bfsbfsbfs 实现,并且还承担着判断残余网络是否联通(有一条从 SSS 到 TTT 的路径)的作用
int find(int u, int limit){
if(u == t){
return limit;
}
int flow = 0;
for(int i = cur[u]; ~i && flow < limit; i = edge[i].nxt){
int v = edge[i].to;
if(dep[v] == dep[u] + 1 && edge[i].w){
int z = find(v, min(edge[i].w, limit - flow));//一条路上最大的流量应小于最小的容量(短板)
if(!z) dep[v] = -1;//若没有增广路,则可以将这个点删掉
edge[i].w -= z;//减去流量
edge[i ^ 1].w += z;//加上流量
flow += z;
}
}
return flow;
}
通过dfs便利,寻找增广路
int dinic(){
int r = 0, flow;
while(bfs()){
while(flow = find(s, INF)){
r += flow;
}
}
return r;
}
重复查找直到没有增广路
代码奉上:
#include <bits/stdc++.h>
#define N 10005
#define M 200005
#define INF 1e8
#define int long long
using namespace std;
int n, m, s, t, u, v, w;
int head[N], tot = 0;
struct node{
int to, w, nxt;
}edge[M];
void add(int u, int v, int w){
edge[tot].w = w;
edge[tot].to = v;
edge[tot].nxt = head[u];
head[u] = tot ++ ;
}
int cur[N], dep[N];
bool bfs(){
memset(dep, -1, sizeof dep);
queue<int> Q;
Q.push(s);
dep[s] = 0;
cur[s] = head[s];
while(Q.size()){
int u = Q.front();
Q.pop();
for(int i = head[u];~i;i = edge[i].nxt){
int v = edge[i].to;
if(dep[v] == -1 && edge[i].w){
dep[v] = dep[u] + 1;
cur[v] = head[v];
if(v == t) return true;
Q.push(v);
}
}
}
return false;
}
int find(int u, int limit){
if(u == t){
return limit;
}
int flow = 0;
for(int i = cur[u]; ~i && flow < limit; i = edge[i].nxt){
int v = edge[i].to;
if(dep[v] == dep[u] + 1 && edge[i].w){
int z = find(v, min(edge[i].w, limit - flow));
if(!z) dep[v] = -1;
edge[i].w -= z;
edge[i ^ 1].w += z;
flow += z;
}
}
return flow;
}
int dinic(){
int r = 0, flow;
while(bfs()){
while(flow = find(s, INF)){
r += flow;
}
}
return r;
}
signed main(){
scanf("%lld %lld %lld %lld", &n, &m, &s, &t);
memset(head, -1, sizeof head);
for(long long i = 1;i <= m;i ++ ){
scanf("%lld %lld %lld", &u, &v, &w);
add(u, v, w);
add(v, u, 0);
}
printf("%lld",dinic());
return 0;
}