zoukankan      html  css  js  c++  java
  • ISAP算法

    为什么叫ISAP

      ISAP(Improved Shortest Augment Path):改进的最短增广路,属于增广路算法

    算法

      Dinic算法中,我们每次都需要BFS出层次图,而在ISAP中,我们只需要初始化时BFS出层次图(从(T)(S)进行),然后在増广的过程中维护标号(d)(就是到汇点(T)的距离)。

      増广的过程和Dinic类似,搜索时沿着(d_v=d_u-1)的边走,走出来的肯定是最短路。不同的是,増广完后,并不会马上对标号(d)进行更新,而是増广到没办法再増广时再更新。具体来说,就是不断BFS找增广路进行増广。如果増广过程中发现没有从(S)(u)再到(T)的增广路,就要对(d_u)进行更新:令(d_u=min_{(u,v)in E'}d_v+1),其中(E')为残留网络的边集。这样可以保证下一次找到的増广路还是最短路。如果(u)没有出边,就令(d_u=n),这样以后増广时就不会经过这个点。更新标号后要往回退一步。

      那什么时候停止呢?当然是不存在(S)(T)的最短路的时候。因为最短路是无环的,所以如果(S)(T)有路径那么一定有(d_Sleq n-1)

      时间复杂度是(O(n^2m)),然而一般不会到这个上界。这就是为什么増广路算法的时间复杂度被称为玄学。

    步骤

    • 初始化,BFS出层次图
    • 每次沿着(d_v=d_u-1)的边进行搜索,找到一条増广路就増广
    • 如果没有从(u)出发到(T)的路径,就更新(d_u),往回退一步
    • (d_Sgeq n)时停止

    一些优化

    • 多次増广。每次找到一条増广路后可以往回退,尝试着找第二条増广路。

    • DFS优化。这样每次找到増广路后就不用把(S)(T)路径的信息全部重新更新一遍了。

    • GAP优化。如果不存在标号为(x)的点,说明残留网络出现了断层。修改标号时如果发现当前点是最后一个(h_u=x)的点,就直接退出。可以维护一个(num)数组,(num_i)表示标号为(i)的点有多少个。

    • 当前弧优化。如果一个点的标号没有被修改,那么这个点已经遍历过的边就不用再遍历了,因为不可能通过这些边再找到増广路。修改一个点的标号时顺便把遍历过的边设为空。实现时维护没遍历过得第一条边。

    • 标号(d)的修改有连续性,即我们不需要找到最小的(d_v),而是直接把(d_u+1)

    • 如果流量用完就退出。

    代码

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cstdlib>
    #include<ctime>
    #include<utility>
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    typedef pair<int,int> pii;
    int h[1000010];
    int v[8000010];
    ll c[8000010];
    int t[8000010];
    int cnt=0;
    void add(int x,int y,ll z)
    {
    	cnt++;
    	v[cnt]=y;
    	c[cnt]=z;
    	t[cnt]=h[x];
    	h[x]=cnt;
    	cnt++;
    	v[cnt]=x;
    	c[cnt]=0;
    	t[cnt]=h[y];
    	h[y]=cnt;
    }
    int op(int x)
    {
    	return ((x-1)^1)+1;
    }
    int S,T;
    int d[1000010];
    int to[1000010];
    int q[1000010];
    int num[1000010];
    int cur[1000010];
    int n,m;
    void bfs()
    {
    	memset(d,-1,sizeof d);
    	d[T]=0;
    	int head=1,tail=0;
    	q[++tail]=T;
    	while(tail>=head)
    	{
    		int i,x=q[head++];
    		num[d[x]]++;
    		for(i=h[x];i;i=t[i])
    			if(c[op(i)]&&d[v[i]]==-1)
    			{
    				d[v[i]]=d[x]+1;
    				q[++tail]=v[i];
    			}
    	}
    }
    int stop;
    ll dfs(int x,ll flow)
    {
    	if(x==T)
    		return flow;
    	ll s=0,u;
    	int &i=cur[x];
    	for(;i;i=t[i])
    		if(d[v[i]]==d[x]-1&&c[i])
    		{
    			u=dfs(v[i],min(c[i],flow));
    			s+=u;
    			flow-=u;
    			c[i]-=u;
    			c[op(i)]+=u;
    			if(stop)
    				return s;
    			if(!flow)
    				return s;
    		}
    	num[d[x]]--;
    	if(!num[d[x]])
    	{
    		d[S]=n;
    		stop=1;
    		return s;
    	}
    	d[x]++;
    	num[d[x]]++;
    	cur[x]=h[x];
    	return s;
    }
    ll maxflow()
    {
    	stop=0;
    	bfs();
    	ll ans=0;
    	memcpy(cur,h,sizeof h);
    	while(d[S]<=n-1)
    		ans+=dfs(S,0x7fffffffffffffffll);
    	return ans;
    }
    int main()
    {
    #ifdef DEBUG
    	freopen("isap.in","r",stdin);
    	freopen("isap.out","w",stdout);
    #endif
    	scanf("%d%d%d%d",&n,&m,&S,&T);
    	int i,x,y;
    	ll z;
    	for(i=1;i<=m;i++)
    	{
    		scanf("%d%d%lld",&x,&y,&z);
    		add(x,y,z);
    	}
    	ll ans=maxflow();
    	printf("%lld
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    IOS-自定义返回按钮,保留系统滑动返回
    IOS-static cell 与 dynamic cell 混合使用
    IOS-快速集成检查更新
    IOS-如何优雅地拦截按钮事件(判断是否需要登录)
    IOS-更优雅地使用Static Cell
    Xcode8出现问题总结
    IOS-工程师Mac上的必备软件
    Minimum Sum of Array(map迭代器)
    C++ STL map
    Friends and Cookies(思维)
  • 原文地址:https://www.cnblogs.com/ywwyww/p/8511443.html
Copyright © 2011-2022 走看看