压入-重标记算法我花了很久才理解,关键是高度函数太坑了。。。这里只说明结论,证明过程参见《算法导论》。

与增广路算法不同的是,此算法不是每次对整张图找增广路,而是处理单个节点,并且允许流量不守恒。

总体的思想就是,将尽可能多的流从源点输出(即尽量将每条管道充满),然后多余的流再返回源点。

那么如何防止流量在两个节点之间反复压入呢?这就需要高度函数来处理。

这里只说明如何处理,具体原理参见《算法导论》。

 1 #include<iostream>
 2 #include<iomanip>
 3 #include<ctime>
 4 #include<climits>
 5 #include<algorithm>
 6 #include<queue>
 7 #include<vector>
 8 #include<cstring>
 9 #include<cstdio>
10 #include<cstdlib>
11 #include<map>
12 using namespace std;
13 typedef unsigned long long LL;
14 #define rep(i,a,b) for(int i=a;i<=b;i++)
15 #define dep(i,a,b) for(int i=a;i>=b;i--)
16 int n,m;
17 const int M=201;
18 int G[M][M];
19 int h[M];//高度函数
20 int e[M];//每个节点当前的余流
21 queue<int>q;
22 int main(){
23 //初始化同增广路算法
24     scanf("%d%d",&n,&m);
25     memset(G,0,sizeof(G));
26     rep(i,1,m){
27         int u,v;
28         scanf("%d%d",&u,&v);
29         scanf("%d",&G[u][v]);
30     }
31     int ans=0;
32     
33     memset(h,0,sizeof(h));
34     int s=1,t=n;
35     h[s]=n;//初始化源点的高度为n,其余各点的高度为0
36     e[s]=INT_MAX;//保证源点有足够的余流
37 //将尽可能多的流直接压入源点相邻的节点(充满管道),并将相邻节点入队
38     rep(i,2,n-1){
39         if(G[s][i]){
40             e[i]=G[s][i];
41             e[s]-=e[i];
42             G[s][i]=0;
43             G[i][s]=e[i];
44             q.push(i);
45         }
46     }
47     if(G[s][t])ans+=G[s][t];//如果源点与汇点直接相连,则直接计入答案
48     while(!q.empty()){
49         int u=q.front();
50         q.pop();
51         int MIN=INT_MAX;//记录当前节点的相邻节点的最小高度
52         rep(i,1,n){
53             int p=min(e[u],G[u][i]);
54             if(!p||h[u]>h[i]+1)continue;//如果当前节点的高度与某个相邻节点的高度差超过1,则将流压入其中不会对答案有贡献,直接跳过(证明见《算法导论》)
55             if(h[u]==h[i]+1){//当且仅当当前节点与相邻节点高度差为1才压入
56                 e[u]-=p;
57                 e[i]+=p;
58                 G[u][i]-=p;
59                 G[i][u]+=p;
60                 if(i==t)ans+=p;
61                 if(i!=s&&i!=t)q.push(i);
62             }
63             else MIN=min(MIN,h[i]);
64         }
65         if(e[u]){//如果当前节点仍有余流,则将其高度改为相邻节点中高于其的最小高度+1
66             h[u]=MIN+1;
67             q.push(u);
68         }
69     }
70     
71     printf("%d",ans);
72 }

复杂度:O(V*V*E)