zoukankan      html  css  js  c++  java
  • Edmonds-Karp算法

    简单介绍一下,( ext{EK})是每次找到一条经过的边数最少的增广路进行流量增广的算法.在每轮寻找增广路的过程中,( ext{EK})算法只考虑图中(f(u, v)<c(u, v))的边,任意一条能从(s)通到(t)的路径都是一条增广路。根据斜对称性,反边都是可以走的。记录下该路径上的最小残量和前驱,到达(t)时可以退出( ext{BFS}),然后从(t)回溯到(s)更新经过的边的容量。时间复杂度:(O(nm^2)),一般能处理(10^3)~(10^4)规模的网络。

    下面证明一下( ext{EK})的复杂度(可以跳过直接看下方代码).

    引理1:

    (f_i)为增广(i)次之后的容许流(即已经选择流过的合法网络),(lambda^k(u,v))表示(f_k)(u)(v)的最短路长度,则:

    [lambda^k(S,v)le lambda^{k+1}(S,v),lambda^k(v,T)lelambda^{k+1}(v,T) ]

    证明:

    假设(f_{k+1})中一条从(S)(v)的最短路为(S ightarrow u_1,cdots, ightarrow u_{x-1} ightarrow u_x,u_x=v,lambda^{k+1}(S,v)=x).
    (e_i=(u_{i-1},u_i)).
    (e_i)(f_k)中同样可用,即(f(u_{i-1}, u_i)<c(u_{i-1}, u_i)),则(lambda^k(S,u_i)le lambda^k(S,u_{i-1})+1);
    (e_i)(f_k)中不可用,则(e_i')((e_i)的反向边)必然可用.而且因为(e_i)(f_k)中不可用,在(f_{k+1})中变成可用,说明(e_i')(f_k)中被进行了增广使得(e_i)可用.也就说明了(e_i')(S)(v)的最短路上,即(lambda^k(S,u_{i-1})= lambda^k(S,u_{i})+1),也满足上面的不等式.
    综上所述,(lambda^k(S,v)=lambda^k(S,u_x)le x=lambda^{k+1}(S,v))

    引理2:

    设边(e)(f_k)变为(f_{k+1})的增广路中,(e')(f_j)变成(f_{j+1})的增广路中((k<j)),则有:

    [lambda^{j}(S,T)ge lambda^{k}(S,T)+2 ]

    证明:

    假设(e=(u,v)),则:(lambda^{k}(S,v)=lambda^{k}(S,u)+1,lambda^{j}(S,T)=lambda^{j}(S,v)+1+lambda^{j}(u,T))
    引理1:
    (lambda^{j}(S,T)ge lambda^{k}(S,v)+1+lambda^{k}(u,T)=lambda^{k}(S,u)+lambda^{k}(u,T)+2=lambda^{k}(S,T)+2)

    (e)(k_1,k_2,cdots,k_x)中在最短增广路上,则必有(j_1,j_2cdots)使得(k_1<j_1<k_2<j_2<cdots),且(e')(j_1,j_2cdots)中在最短增广路上.因为(1le lambda^{k_1}(S,T),lambda^{k_x}le n),所以(xlefrac{n+2}{4}).即每条边最多被增广(frac{n+2}{4})次,而每次增广的复杂度是(O(m))的,总的复杂度即为(O(frac{n+2}{4}*m*m)=O(m^2n)).
    证毕.

    代码:

    #include <cstdio>
    #include <cstring>
    const int maxn=10000+10;
    const int maxm=100000+10;
    const int INF=0x3f3f3f3f;
    int head[maxn],to[maxm<<1],nxt[maxm<<1],val[maxm<<1];
    int tot=1,maxflow=0;
    int pre[maxn],minf[maxn];
    int n,m,s,t;
    struct Queue
    {
    	int a[maxn];
    	int l,r;
    	Queue() {l=1,r=0;}
    	void push(int x) {a[++r]=x;}
    	void pop() {l++;}
    	int front() {return a[l];}
    	bool empty() {return l>r;}
    }q;
    
    int min(int x,int y) {return x<y?x:y;} 
    void add(int u,int v,int w)
    {
    	nxt[++tot]=head[u];
    	head[u]=tot;
    	to[tot]=v;
    	val[tot]=w;
    }
    bool bfs()
    {
    	memset(pre, 0, sizeof(pre));
    	pre[s]=-1;
    	minf[s]=INF;
    	q=Queue();
    	q.push(s);
    	while(!q.empty())
    	{
    		int u=q.front();
    		q.pop();
    		for (int i=head[u];i;i=nxt[i])
    		{
    			int v=to[i];
    			if (pre[v]||!val[i])
    				continue;
    			pre[v]=i;
    			minf[v]=min(minf[u], val[i]);
    			q.push(v);
    			if (v==t)
    				return 1;
    		}
    	}
    	return 0;
    }
    void update()
    {
    	int u=t,d=minf[t];
    	while(u!=s)
    	{
    		int i=pre[u];
    		val[i]-=d;
    		val[i^1]+=d;
    		u=to[i^1];
    	}
    	maxflow+=d; 
    }
    int main()
    {
    	scanf("%d%d%d%d",&n,&m,&s,&t);
    	for (int i=1;i<=m;i++) 
    	{
    		int u,v,w;
    		scanf("%d%d%d",&u,&v,&w);
    		add(u, v, w),add(v, u, 0);
    	}
    	while(bfs())
    		update();
    	printf("%d
    ",maxflow);
    	return 0;
    }
    
  • 相关阅读:
    java字符串的遍历以及字符串中各类字符的统计
    Java Jvm运行机制原理
    为什么面试要问 hashmap 的原理
    HashMap的实现原理
    redis两种持久化方式的优缺点
    2018No-java面试知识
    从架构演进的角度聊聊spring cloud都做了些什么?
    MySQL索引优化
    2018java面试知识汇总
    多线程—7种同步方法
  • 原文地址:https://www.cnblogs.com/hkr04/p/Edmonds-Karp.html
Copyright © 2011-2022 走看看