zoukankan      html  css  js  c++  java
  • 网络流初步

    网络流初步

    P.S.

    简单的学习了一下,以后方便复习。

    相关概念

    • 源点:只有出边没有入边的点。
    • 汇点:只有入边没有出边的点。
    • 容量和流量:每条有向边上有两个量,容量和流量。从i到j的容量通常用c(i,j)表示,流量则通常是f(i,j)。

    相关性质

    • 容量限制:f(u,v)≤c(u,v)
    • 反对称性:f(u,v) = - f(v,u)
    • 流量守恒:对于不是源点也不是汇点的任意结点,流入该结点的流量和等于流出该结点的流量和。

    最大流

    对于一个网络,合法的流中,使每一条边的流量之和最大的流。

    EK增广路算法:

    该算法的核心在于:

    用bfs寻找一条从源点到汇点的增广路,并记录下这条路上最小的剩余容量,作为本条路上本次通过的流量。

    然后直到找不到这样的增广路时,就结束算法,此时得到最大流。

    但是注意到每次寻找是随机的,这样会对后续造成影响,从而可能得不到真正的最大流。

    所以需要给每个流反悔的机会,即“退流”,用于给更好的方案让路。

    这可以用建立反向边来实现,当正向边经过一个流时,反向边对应的增加这个流的流量即可。

    dinic算法:

    该算法是基于对EK的优化,可以发现EK的每次bfs都只寻找到了一条增广路,而同时却可能遍历整个残量网络。

    而dinic则是每次遍历完残量网络后,找到了多条增广路,并完成了流量的更新。

    核心内容:

    首先bfs在原图上建立分层图。然后沿着分层图多路增广即可。

    分层图的构造:

    IL int bfs() {
    	while(!q.empty()) q.pop();
    	memset(d,0,sizeof(d));
    	d[S]=1,q.push(S);
    	while(!q.empty()) {
    		RG int i,y,x=q.front(); q.pop();
    		for(i=head[x];i;i=e[i].next)
    			if(e[i].v&&!d[y=e[i].to]) {
    				d[y]=d[x]+1,q.push(y);
    				if(y==T) return 1;
    			}
    	}
    	return 0;
    }
    

    当前弧优化:

    在固定的一张分层图中,到达一个点的路径是固定的。所以在这种情况下到达了一次某个点后,之前走过的这个点的出边

    就没有必要再走了,所以直接记录一下这个点下一条该走的出边即可。由于它是对于一张固定的分层图才能这样,所以每

    次bfs重建分层图后需要重置该数组。

    
    int dfs(int x,int flow) {
    	if(x==T) return flow;
    	RG int i,y,k,rest=flow;
    	for(i=cur[x];i&&rest;i=e[i].next)
    		if(d[y=e[i].to]==d[x]+1&&e[i].v) {
                cur[x]=i; // Here 
    			k=dfs(y,min(rest,e[i].v));
    			if(!k) d[y]=0;
    			rest-=k,e[i].v-=k,e[i^1].v+=k;
    		}
    	return flow-rest;
    }
    
    IL void dinic() {
    	RG int i,j,flow,maxflow=0;
        while(bfs()) {
            memcpy(cur,head,sizeof(head)); //reset
            while(flow=dinic(S,inf)) maxflow+=flow;
        }
    }
    
    

    最小割

    最大流最小割定理:一个网络最大流量=最小割中边的容量之和。

    费用流

    注意此处涉及的费用流是先最大流再最小(大)费用的情况。

    一般有spfa-EK或spfa-dinic来求解。即把二者的spfa代替掉bfs即可。

    比如dinic费用流的分层图构建部分:

    
    IL int spfa() {
        memset(d,0x3f,sizeof(d));
        while(!q.empty()) q.pop();
        d[S]=0,q.push(S);
        while(!q.empty()) {
            RG int i,y,x=q.front();
            in[x]=0,q.pop(); //spfa 可以多次入队!
            for(i=head[x];i;i=e[i].next)
                if(e[i].v&&d[y=e[i].to]>d[x]+e[i].w) {
                    d[y]=d[x]+e[i].w;
                    if(!in[y]) in[y]=1,q.push(y);
                }
        }
        return d[T]!=inf;
    }
    

    the end

    这大概就是基础网络流知识了,关于上下界网络流的学习就看这里吧。

  • 相关阅读:
    Android Studio的project中两个build.gradle配置的区别
    build script和all projects作用和区别
    让overflow:auto页面滚动条出现时不跳动
    GreenDao设置数据版本
    Greendao 缓存问题
    js中文编码到C#后台解码
    content-type: application/json没有设置导致的500错误
    Android的Device File Explorer刷新文件
    adb server version (31) doesn't match this client (40); killing...
    Sqlserver远程过程调用失败
  • 原文地址:https://www.cnblogs.com/Bhllx/p/11235607.html
Copyright © 2011-2022 走看看