Sorry, your browser cannot access this site
This page requires browser support (enable) JavaScript
Learn more >

算法介绍

最大流问题是指在一个有向图中,从一个源点到一个汇点,最大化流量,使得所有边的容量都大于等于 0。

dinic 算法是一种高效的求解最大流问题的算法,其时间复杂度为 \(O(EV^2)\)

dinic 算法的基本思想是,通过增广路算法,找到一条从源点到汇点的增广路,然后通过深度优先搜索(DFS)算法,在增广路上进行流的增广,直到不能再增广为止。

步骤

  1. 初始化 flow(最大流量)为 INF(极大值),建边(正向弧和反向弧)
  2. bfs 寻找增广路看看有没有路,顺便进行深度标号。如果没有路直接结束输出 flow
  3. 如果有,我们按照深度 dfs。dfs 时注意在给正向弧减权时给反向弧加权。
  4. ans+=flow ,重复2到4步骤,直到无路可走。

以上就是网络流全部内容(误

P3376 【模板】网络最大流

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
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#define R register
#define INF 1<<30
using namespace std;
template <typename T>
inline T read()
{
T x=0;int w=0;char c=getchar();
while(!isdigit(c))w|=c=='-',c=getchar();
while(isdigit(c))x=(x<<3)+(x<<1)+(c^48),c=getchar();
return w?-x:x;
}
namespace star
{
typedef long long ll;
const int maxn=100000;
struct Edge{
int to,nxt;
ll dis;
}e[200000];
int head[maxn],ecnt=1;//这里记为-1的原因时便于访问正向弧和反向弧
int n,m,s,end,dep[maxn];
inline void addedge(int from,int to,ll dis){
e[++ecnt].to=to,e[ecnt].nxt=head[from],e[ecnt].dis=dis,head[from]=ecnt;
}
inline void add(int from,int to,ll dis){
addedge(from,to,dis);addedge(to,from,0);
}
ll DFS(int x,ll flow)
{
if(x==end) return flow;
ll used=0;
for(R int i=head[x];i;i=e[i].nxt)
{
int u=e[i].to;
if(e[i].dis and dep[u]==dep[x]+1)
{
long long w=DFS(u,min(e[i].dis,flow-used));
used+=w;
e[i].dis-=w;e[i^1].dis+=w;
if(used==flow)return flow;
}
}
if(!used)dep[x]=-1;
return used;
}
queue<int>q;
inline bool BFS()
{
memset(dep,-1,sizeof dep);
dep[s]=0;
q.push(s);
while(!q.empty())
{
int u=q.front();q.pop();
for(R int i=head[u];i;i=e[i].nxt)
if(e[i].dis and dep[e[i].to]==-1)
dep[e[i].to]=dep[u]+1,q.push(e[i].to);
}
if(dep[end]==-1)return 0;
return 1;
}
inline void work()
{
n=read<int>(),m=read<int>(),s=read<int>(),end=read<int>();
ll ans=0;
int a,b;
ll c;
for(R int i=0;i<m;i++)a=read<int>(),b=read<int>(),c=read<ll>(),add(a,b,c);
while(BFS())
ans+=DFS(s,INF);
printf("%lld\n",ans);
}
}
signed main()
{
star::work();
return 0;
}

当前弧优化

如果虽然上面的代码能过模板题,但是时间复杂度其实是不对的,因为没有加当前弧优化。

这也不是什么高深的玩意。我们证明,如果一个点在之前的 dfs 中已经把一些边考虑过了,由于在当前和以后的流的 dfs 中这些边都是增广过的,也就是再也没法做出贡献了,那么我们用一个数组 cur 记录一下考虑到哪里了然后下一次 dfs 时接着上次的就行了。

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
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#define R register
#define INF 1<<30
using namespace std;
template <typename T>
inline T read()
{
T x=0;int w=0;char c=getchar();
while(!isdigit(c))w|=c=='-',c=getchar();
while(isdigit(c))x=(x<<3)+(x<<1)+(c^48),c=getchar();
return w?-x:x;
}
namespace ysr
{
typedef long long ll;
const int maxn=100000;
struct Edge{
int to,nxt;
ll dis;
}e[200000];
int cur[maxn],head[maxn],ecnt=1;//这里记为-1的原因时便于访问正向弧和反向弧
int n,m,s,end,dep[maxn];
inline void addedge(int from,int to,ll dis){
e[++ecnt].to=to,e[ecnt].nxt=head[from],e[ecnt].dis=dis,head[from]=ecnt;
}
inline void add(int from,int to,ll dis){
addedge(from,to,dis);addedge(to,from,0);
}
ll DFS(int x,ll flow)
{
if(x==end) return flow;
ll used=0;
for(R int i=cur[x];i;i=e[i].nxt)
{
cur[x]=i;
int u=e[i].to;
if(e[i].dis and dep[u]==dep[x]+1)
{
long long w=DFS(u,min(e[i].dis,flow-used));
used+=w;
e[i].dis-=w;e[i^1].dis+=w;
if(used==flow)return flow;
}
}
if(!used)dep[x]=-1;
return used;
}
queue<int>q;
inline bool BFS()
{
for(R int i=0;i<=n;i++)cur[i]=head[i],dep[i]=-1;
dep[s]=0;
q.push(s);
while(!q.empty())
{
int u=q.front();q.pop();
for(R int i=head[u];i;i=e[i].nxt)
if(e[i].dis and dep[e[i].to]==-1)
dep[e[i].to]=dep[u]+1,q.push(e[i].to);
}
if(dep[end]==-1)return 0;
return 1;
}
inline void work()
{
n=read<int>(),m=read<int>(),s=read<int>(),end=read<int>();
ll ans=0;
int a,b;
ll c;
for(R int i=0;i<m;i++)a=read<int>(),b=read<int>(),c=read<ll>(),add(a,b,c);
while(BFS())
ans+=DFS(s,INF);
printf("%lld\n",ans);
}
}
signed main()
{
ysr::work();
return 0;
}

给小狼留言