网络流之最大流
暑假就已经学过了,可是因为我太菜了,所以完全没懂,今天老师要我们落实之前学的,就复习了网络流
虽然CSP不考,但是学了也可以用(就像去年D2T3,动态DP模板)
好了,接下来进入正题:
首先,啥子事网络流,官方解释如下
在图论中,网络流(英语:Network flow)是指在一个每条边都有容量(capacity)的有向图分配流,使一条边的流量不会超过它的容量。
通常在运筹学中,有向图称为网络。顶点称为节点(node)而边称为弧(arc)。一道流必须匹配一个结点的进出的流量相同的限制,除非这是一个源点(source)──有较多向外的流,
或是一个汇点(sink)──有较多向内的流。一个网络可以用来模拟道路系统的交通量、管中的液体、电路中的电流或类似一些东西在一个结点的网络中游动的任何事物。
嘿嘿,好像不懂,简单一点来说:
有一个储水站,给一个小区分配水,而你又住在里面
每家每户都有不同规格的水管,也就是说,在速度不变的情况下,同一时间内,流动的水的多少不同
而且从储水站里面送出来的速度必须一样,因为流速越快,液体压强越大
我们当然不希望水管炸裂
每一户人家用完的水肯定要运输到一个废水处理站
我们需要求的是储水站最多可以送出多少水,最后能够送到废水处理站(因为要回收利用)
我第一次看到这个题目时,产生了一段对话:
我:这个不就是暴力枚举吗???
某巨佬:......这个超时了
我:......那怎么做
某巨佬:EK或者是Dinic
我:???什么玩意儿
首先明确几个概念:
容量:每条边都有一个容量(水管的最大水流容量)
源点:出发点(储水站)。
汇点:结束点(废水处理站)。
流:一个合法解称作一个流,也就是一条可以从源点到汇点的一条合法路径。
流量:每条边各自被经过的次数称作其流量,最终收集的总数为整个流的流量。
增广路:在当前网络之后找到一条能够从源点到汇点能运更多货物的路径。当一条边被增广之后(即它是增广路的一部分,或者说增广路通过这条边),这条边还能通过的流量,叫做剩余流量,修改之后的图称为残量网络。
首先是我最喜欢的EK算法(主要是代码易懂,简洁,适合我这种蒟蒻)
它的思想很简单,每一次疯狂的寻找增广路,当然如果其中有一条边为‘0’,就不是增广路,将当前的流量减去中间的最小值(肯定的,因为如果不是最小值水就不能通过)
ans加上当前这个最小值,直到寻找不到增广路
但是有瑕疵,可以把这个hack掉:
我们可以轻易知道,答案为2,但是如果按照以上思想:
程序会这样跑,以至于ans=1:
所以,我们需要给这个程序一个后悔的机会
是不是想到dfs回溯,很明显,超时了
所以我们想到了一个巧妙的办法,建反边,初始值为零
在找增广路的时候,设这条路的最小流量为opt
那么,反边的值+opt,正边-opt
我们可以这么想,我们走反的边相当于将水退了回去
好的,就这样,AC代码如下:
#include<bits/stdc++.h> using namespace std; const int N=300002; int spot,EDGE,S,E; int head[N],ver[N],nxt[N],tot,edge[N]; int q[N],hd,tl,res[N],now[N],pre[N],ans; int read(){ int s=0,w=1;char ch=getchar(); while(ch<'0'||ch>'9')w=(ch=='-')?-1:1,ch=getchar(); while(ch>='0'&&ch<='9')s=s*10+ch-'0',ch=getchar(); return s*w; } void write(int x){ if(x<0) {putchar('-');x=-x;} int y=10,len=1; while(y<=x) {y*=10;len++;} while(len--){y/=10;putchar(x/y+48);x%=y;} } void add(int x,int y,int z){ ver[++tot]=y;edge[tot]=z;nxt[tot]=head[x];head[x]=tot; } bool Bfs(){ memset(res,0,sizeof(res)); hd=0;tl=1;q[1]=S;res[S]=1;now[S]=1e9; while(hd^tl){ hd++; int x=q[hd]; for(int i=head[x];i;i=nxt[i]){ int y=ver[i]; if(!res[y]&&edge[i]>0){ now[y]=min(now[x],edge[i]);pre[y]=i; if(y==E)return 1; tl++;q[tl]=y;res[y]=1; } } } return 0; } void EK(){ ans+=now[E]; int x=E; while(x!=S){ edge[pre[x]]-=now[E];edge[pre[x]+1]+=now[E]; x=ver[pre[x]+1]; } } int main(){ EDGE=read();S=1;E=read(); for(int i=1;i<=EDGE;i++){ int x=read(),y=read(),z=read(); add(x,y,z);add(y,x,0); } while(Bfs())EK(); write(ans); return 0; }
// 10-2 //
原文地址:https://www.cnblogs.com/GMSD/p/11617827.html
- Python交互式数据分析报告框架:Dash
- PyQt5 GUI应用程序工具包入门(2)
- 用Python研究了三千套房子,告诉你究竟是什么抬高了房价?
- 设计模式之装饰模式
- Android网络编程(六)OkHttp3用法全解析
- Android网络编程(五)OkHttp用法全解析
- Android网络编程(一)HTTP协议原理
- Spring Cloud实战小贴士:Zuul统一异常处理(二)
- Android View体系(九)自定义View
- Android网络编程(二)HttpClient与HttpURLConnection
- Spring Batch入门篇
- Android网络编程(三)Volley用法全解析
- Android网络编程(八)源码解析OkHttp中篇[复用连接池]
- Mybatis SqlSessionTemplate 源码解析
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Notes | GitHub Upload Large Files
- PPT 中插入图片的几个小技巧
- Stata | 整理调查问卷修改内容清单
- LaTeX | 绘制微观经济学图形
- Python | 地址数据清理相关的库
- 【Redis数据结构 序】使用redis-py操作Redis数据库
- 【Redis数据结构 String类型】String类型生产中的应用 缓存、计数器、限速器的实现
- Redis数据结构 List 类型】List 类型生产中的应用 消息队列、排行榜、朋友圈、监控程序的实现
- Crawlab准备之python+scrapy环境搭建
- 一致性协议之 ZAB
- Elasticsearch Analyzer原理分析并实现中文分词
- php laravel5.5使用rabbitmq消息队列
- 还不知道怎么运行Python代码,快来撸代码
- Linux常用命令速查表
- 机器学习中必须要会用的模块