压入-重标记算法我花了很久才理解,关键是高度函数太坑了。。。这里只说明结论,证明过程参见《算法导论》。
与增广路算法不同的是,此算法不是每次对整张图找增广路,而是处理单个节点,并且允许流量不守恒。
总体的思想就是,将尽可能多的流从源点输出(即尽量将每条管道充满),然后多余的流再返回源点。
那么如何防止流量在两个节点之间反复压入呢?这就需要高度函数来处理。
这里只说明如何处理,具体原理参见《算法导论》。
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)