zoukankan      html  css  js  c++  java
  • [笔记]网络流--最大流问题

    [笔记]网络流--最大流问题

    原题链

    算法用途

    ​ 解决网络流最大流问题,一般使用的是EK算法或是Dinic算法,由于Dinic的复杂度更优秀,所以我用的是Dinic,这里也只讲这个算法EK不会啊

    问题描述

    一.几个概念

    ​ 1.网络:一个网络 (G = (V,E))是一张有向图,图中每条有向边((x,y) ∈ E)都有一个给定的权值(c(x,y)),称为变的容量.图中还有两个特殊的给定的点(S,T)称为源点和汇点,也就是起点和终点.(f(x,y))函数是指一个满足条件的流量方案,满足:

    [f(x,y) ≤ c(x,y)\ f(x,y) = -f(y,x)\ ]

    (f)函数也可以成为边的流量.

    ​ 2.最大流问题:对于一个给定的网络,合法的流函数(f)有很多,使得整个网络流量(Σ_{(s,v)∈E}f(s,v))最大,(S是源点)的流函数称为网络的最大流.

    算法描述

    一.大致流程

    ​ 1.用BFS找出可行的增广路(也就是可以增加流量的从源点到汇点的路径),并预处理出各个节点的深度.

    ​ 2.再用dfs对增广路增加流量,知道不存在增广路.

    ​ 3.重复一上两个步骤知道不能增广为止.

    二.具体实现

    ​ 首先对每条弧存一条反向弧,初始流量为0,当正向弧剩余流量减少时,反向弧剩余流量随之增加,这样就为每条弧提供了一个反悔的机会,可以让一个流沿反向弧退回而去寻找更优的路线.对于一个网络流图,用bfs将图分层,只保留每个点到下一个层次的弧,目的是减少寻找增广路的代价.对于每一次可行的增广操作,用dfs的方法寻找一条由源点到汇点的路径并获得这条路径的流量c.根据这条路径修改整个图,将所经之处正向边流量减少c,反向边流量增加c.如此反复直到bfs找不到可行的增广路线.

    三.优化

    当前弧优化

    ​ 对于一个节点(x),当它在(dfs)中走到了第(i)条弧时,前(i-1)条弧到汇点的流一定已经被流满而没有可行的路线了.那么当下一次再访问(x)节点的时候,前(i-1)条弧就可以被删掉而没有任何意义了.所以我们可以在每次枚举节点(x)所连的弧时,改变枚举的起点,这样就可以删除起点以前的所有弧以达到优化的效果.

    整体流量优化

    ​ 对于每一个节点,记录可以向下流的总流量再返回,就可以减少递归的次数.

    代码

    #include <bits/stdc++.h>
    using namespace std;
    struct node{
    	long long to = 0,next = 0,w = 0;
    }edge[50010];
    long long fir[50010],cur[50010];
    long long n,m,s,t,tot = 1;
    long long dep[50010];
    long long maxx = 0;
    const int inf = INT_MAX;
    void add(long long x,long long y,long long z){
    	tot++;
    	edge[tot].to = y;
    	edge[tot].w = z;
    	edge[tot].next = fir[x];
    	fir[x] = tot;
    	tot++;                 //建反向边方便反悔
    	edge[tot].to = x;
    	edge[tot].w = 0;
    	edge[tot].next = fir[y];
    	fir[y] = tot;
    	return;
    }
    bool bfs(){
    	queue < int > q;
    	while(!q.empty())q.pop();
    	memset(dep,0,sizeof(dep));
    	q.push(s);dep[s] = 1;
    	while(!q.empty()){
    		int x = q.front();
    		q.pop();
    		for(int i = fir[x];i;i = edge[i].next){
    			if(edge[i].w > 0 && dep[edge[i].to] == 0){
    				q.push(edge[i].to);
    				dep[edge[i].to] = dep[x] + 1;
    				if(edge[i].to == t)return true;    //找到增广路
    			}
    		}
    	}
    	return false;
    }
    long long dinic(int x,long long flow){
    	if(x == t)return flow;
    	long long now = 0;
    	for(int i = cur[x];i && flow;i = edge[i].next){
    		cur[x] = i;      //记录当前搜的点,当前弧优化
    		if(edge[i].w != 0 && dep[edge[i].to] == dep[x] + 1){
    			int k = dinic(edge[i].to,min(edge[i].w,flow));
    			if(k == 0)dep[edge[i].to] = 0;
    			now += k;
    			edge[i].w -= k;
    			edge[i ^ 1].w += k;
    			flow -= k;
    		}
    	}
    	return now;
    }
    int main(){
    	scanf("%lld%lld%lld%lld",&n,&m,&s,&t);
    	for(int i = 1;i <= m;i++){
    		long long x,y,z;
    		scanf("%lld%lld%lld",&x,&y,&z);
    		add(x,y,z);
    	}
    	while(bfs()){
    		memmove(cur,fir,sizeof(cur));
    		maxx += dinic(s,inf);
    	}
    	printf("%lld
    ",maxx);
    	return 0;
    }
    
    
  • 相关阅读:
    进程 线程
    random模块 时间模块 sys模块 os模块 json模块 pickle模块
    异常处理
    面向对象进阶篇
    面向对象初级篇
    正则表达式
    re模块的相关知识
    CNN归纳偏好
    window和Linux下安装nvidia的apex
    使用GrabCut做分割
  • 原文地址:https://www.cnblogs.com/czy--blog/p/13570186.html
Copyright © 2011-2022 走看看