0%

网络流

最大流

EK算法

通过bfs()查找增广路来更新最大流

关键思想

lst:在bfs过程中找到的增广路的某个点,它上个点的索引

pre:在bfs过程中找到的增广路的某个点,它上个点与自己连成的边在上个点的邻接表中的索引

flow:某个点的通过的流量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#include <bits/stdc++.h>
#define int long long
using namespace std;

const int MAXN=2e2+2;
const int INF=0x3f3f3f3f3f3f3f3f;

struct edge_t{
int v,w,rid;
};

int n,m,s,t;
vector<edge_t> G[MAXN];
bool vis[MAXN];
int lst[MAXN],pre[MAXN],flow[MAXN];

inline void add(int u,int v,int w){
int id=G[u].size();
int rid=G[v].size();
G[u].push_back({v,w,rid});
G[v].push_back({u,0,id});
}

bool bfs(){
memset(vis,0,sizeof vis);
queue<int> q;
q.push(s);
vis[s]=true;
flow[s]=INF;
while(!q.empty()){
int u=q.front();q.pop();
for(int i=0;i<G[u].size();++i){
int v=G[u][i].v,w=G[u][i].w;
if(!vis[v]&&w>0){
vis[v]=true;
flow[v]=min(flow[u],w);
lst[v]=u;
pre[v]=i;
if(v==t)return true;
q.push(v);
}
}
}
return false;
}

int ek(){
int res=0;
while(bfs()){
res+=flow[t];
for(int i=t;i!=s;i=lst[i]){
int u=lst[i],v=i,rid=G[u][pre[v]].rid;
G[u][pre[v]].w-=flow[t];
G[v][rid].w+=flow[t];
}
}
return res;
}

signed main(){
cin>>n>>m>>s>>t;
for(int i=1,u,v,w;i<=m;++i){
cin>>u>>v>>w;
add(u,v,w);
}
cout<<ek()<<'\n';
}

Dinic算法

通过bfs()分层后,考虑当前弧优化和剪枝的情况下做dfs()去更新最大流,下面dfs()有两种写法,推荐未注释写法。

关键思想

depbfs分层结果

cur:当前弧优化,就是将不需要搜的边排除掉所用到的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
#include <bits/stdc++.h>
#define int long long
using namespace std;

const int MAXN=2e2+2;
const long long INF=0x3f3f3f3f3f3f3f3f;

int n,m,s,t;
int dep[MAXN],cur[MAXN];

struct edge_t{
int v,w,rid;
};

vector<edge_t> G[MAXN];

inline void add(int u,int v,int w){
int id=G[u].size();
int rid=G[v].size();
G[u].push_back({v,w,rid});
G[v].push_back({u,0,id});
}

bool bfs(){
memset(dep,-1,sizeof dep);
queue<int> q;
q.push(s);dep[s]=0;
while(!q.empty()){
int u=q.front();q.pop();
for(auto&& [v,w,rid]:G[u])if(dep[v]==-1&&w>0){
dep[v]=dep[u]+1;
q.push(v);
}
}
return dep[t]!=-1;
}

// int dfs(int u,int flow){
// if(u==t)return flow;
// for(int i=cur[u];i<G[u].size();++i){
// cur[u]=i;
// int v=G[u][i].v,w=G[u][i].w,rid=G[u][i].rid,res;
// if(dep[v]==dep[u]+1&&w>0){
// if(res=dfs(v,min(w,flow))){
// G[u][i].w-=res;
// G[v][rid].w+=res;
// return res;
// }else dep[v]=-1;
// }
// }
// return 0;
// }

int dfs(int u,int flow){
if(u==t)return flow;
int ans=0,res;
for(int i=cur[u];i<G[u].size()&&flow>0;++i){
cur[u]=i;
int v=G[u][i].v,w=G[u][i].w,rid=G[u][i].rid;
if(dep[v]==dep[u]+1&&w>0){
res=dfs(v,min(flow,w));
if(res==0)dep[v]=-1;
G[u][i].w-=res;
G[v][rid].w+=res;
ans+=res;
flow-=res;
}
}
return ans;
}

int dinic(){
int ans=0,res;
while(bfs()){
memset(cur,0,sizeof cur);
while(res=dfs(s,INF))ans+=res;
}
return ans;
}

signed main(){
cin>>n>>m>>s>>t;
for(int i=1,u,v,w;i<=m;++i){
cin>>u>>v>>w;
add(u,v,w);
}
cout<<dinic()<<'\n';
}

最小割

最小割等于最大流

最小费用最大流

最大流情况下的最小费用(因为最大流的分配情况不唯一),实现就是用EK算法的基础将bfs改成最短路spfa

关键思想(EK之外的)

dis:当前增广路的单位费用和

visspfa算法中的入队判断

反向建边的单位费用为相反数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#include <bits/stdc++.h>
//ek中bfs改成spfa
using namespace std;

const int MAXN=5e3+10;
const int INF=0x3f3f3f3f;

int n,m,s,t,minc,maxf;
int flow[MAXN],lst[MAXN],pre[MAXN];
int dis[MAXN],vis[MAXN];

struct edge_t{
int v,w,c,rid;
};

vector<edge_t> G[MAXN];

inline void add(int u,int v,int w,int c){
int id=G[u].size();
int rid=G[v].size();
G[u].push_back({v,w,c,rid});
G[v].push_back({u,0,-c,id});
}

bool spfa(){
queue<int> q;
memset(dis,0x3f,sizeof dis);
memset(vis,0,sizeof vis);
q.push(s);dis[s]=0;vis[s]=1;flow[s]=INF;
while(!q.empty()){
int u=q.front();q.pop();vis[u]=0;
for(int i=0;i<G[u].size();++i){
int v=G[u][i].v,w=G[u][i].w,c=G[u][i].c,rid=G[u][i].rid;
if(dis[v]>dis[u]+c&&w>0){
dis[v]=dis[u]+c;
flow[v]=min(flow[u],w);
lst[v]=u;
pre[v]=i;
if(!vis[v])q.push(v),vis[v]=1;
}
}
}
if(dis[t]==INF)return false;
return true;
}

void mcmf(){
while(spfa()){
maxf+=flow[t];
minc+=dis[t]*flow[t];
for(int i=t;i!=s;i=lst[i]){
int u=lst[i],v=i,rid=G[u][pre[v]].rid;
G[u][pre[v]].w-=flow[t];
G[v][rid].w+=flow[t];
}
}
}

int main(){
cin>>n>>m>>s>>t;
for(int i=1,u,v,w,c;i<=m;++i){
cin>>u>>v>>w>>c;
add(u,v,w,c);
}
mcmf();
cout<<maxf<<' '<<minc<<'\n';
return 0;
}