### P3376 题目链接 ###

这里讲一下三种优化的实现以及正确性。

1、dfs多路增广优化

一般的Dinic算法中是这样的,bfs() 用于标记多条增广路,以至于能一次 bfs() 出多次 dfs()增广路。那么就会有 while(bfs()) 一次,然后 dfs() n 次,出 n 条增广路。

那么我们 dfs 的优化在于使得一次 dfs() 直接累加出这一次 bfs() 所标记出的 n 条增广路。变成 while(bfs())然后 dfs() 一次 即可。

一般的 Dinic 算法:

int dfs(int u,int res)
{
if(u==T) return res;
for(int i=head[u];i!=-;i=edge[i].next){
int v=edge[i].to;
if(flag[v]==flag[u]+&&edge[i].val){
if(int k=dfs(v,min(res,edge[i].val))){
edge[i].val-=k;
edge[i^].val+=k;
return k;
}
}
}
return ;
}

这里的 return k 保证了每次 dfs() 到达终点 T 时,出一次增广路的流量 k ,然后就返回结束了这次 dfs() 。

那我们需要做的是,使得 dfs() 搜到所有 增广路 后才 return 。

那么可以这样,我们用 nowflow 表示当前点所经历过的流量。我们看下图,比如现在 u == 2 时,1---> 2 的流量限制为 10 ,那么在每次返回到 u 点时,u 点所能经过的流量不能大于 10 ,否则这条路将不会成为增广路。那么用 nowflow 来累加出 u 点的流量,一旦 nowflow 等于 10 ,则增广路将无法再从 u 点伸展,故 break 即可。

对于 min 中的 flow - nowflow ,这里是为了告诉 u 后面的所有点的总流量不能大于 flow - nowflow ,即 u 点所能承载的最大流量。

然后对于返回值的问题:需要返回该点的流量。原因是因为,如果该点被伸展完后,那么一直返回该点的流量,直到返回到起点 S 的 nowflow 值为 0  ,则此时的 nowflow 就表示这次 bfs() 所能找到的所有增广路的流量和。

int dfs(int u,int flow)
{
int nowflow=;
if(u==T) return flow;
for(int i=head[u];i!=-;i=edge[i].next){
cur[u]=i;
int v=edge[i].to;
if(d[v]==d[u]+&&edge[i].val>){
if(int k=dfs(v,min(flow-nowflow,edge[i].val))){
edge[i].val-=k;
edge[i^].val+=k;
nowflow+=k;
if(nowflow==flow) break;
}
}
}
return nowflow;//这里需要返回该点的总流量
}

2、炸点优化

如果当前 u 点伸展完后发现过这个点没有任何增广路被发现,即当前点 nowflow ,则说明增广路不可能通过该点伸展,故直接把这个点 “炸” 掉,即 d[u] = -1 。

int dfs(int u,int flow)
{
int nowflow=;
if(u==T) return flow;
for(int i=head[u];i!=-;i=edge[i].next){
cur[u]=i;
int v=edge[i].to;
if(d[v]==d[u]+&&edge[i].val>){
if(int k=dfs(v,min(flow-nowflow,edge[i].val))){
edge[i].val-=k;
edge[i^].val+=k;
nowflow+=k;
if(nowflow==flow) break;
}
}
}
if(!nowflow) d[u]=-; // 炸点
return nowflow;
}

3、当前弧优化

对于一次 bfs() 后寻找增广路来说,在 dfs() 时如果询问了一条路的编号为 a 时,那么不再会通过这条路增广。

比如这里一开始已经通过 1 → 2 → 3 进行了增广,流量累加了。那么下次再遍历到 点 2 时,2 → 3 这条路是不会再提供流量的,所以可以直接排除掉(当然它可能走 3 → 2 这条相应的反向边,反正边的 id 不一样,没影响)

这样我们用 cur[u] 来表示当前 u 点所延伸到的最后一个边的序号,然后下次遍历到 u 点时,直接从这条边开始遍历。

int dfs(int u,int flow)
{
int nowflow=;
if(u==T) return flow;
for(int i=cur[u];i!=-;i=edge[i].next){
cur[u]=i; // 当前弧优化
int v=edge[i].to;
if(d[v]==d[u]+&&edge[i].val>){
if(int k=dfs(v,min(flow-nowflow,edge[i].val))){
edge[i].val-=k;
edge[i^].val+=k;
nowflow+=k;
if(nowflow==flow) break;
}
}
}
if(!nowflow) d[u]=-;
return nowflow;
}

这三种优化会使得Dinic时间复杂度降低很多,这边测试是直接降了接近 300 MS 。

总代码如下:

#define IO freopen("test.in","r",stdin),freopen("test.out","w",stdout)
#define inf 0x3f3f3f3f
#define lson root<<1
#define rson root<<1|1
#include <set>
#include <map>
#include <deque>
#include <queue>
#include <stack>
#include <cmath>
#include <ctime>
#include <bitset>
#include <cstdio>
#include <string>
#include <vector>
#include <cstdlib>
#include <cstring>
#include <cassert>
#include <iostream>
#include <algorithm>
#include <unordered_map>
using namespace std;
#define maxn 10008
#define inf 0x3f3f3f3f
int N,M,S,T,cnt;
int head[maxn],cur[maxn],d[maxn];
inline int read()
{
int x=,w=;char ch=;
while(!isdigit(ch)){w|=ch=='-';ch=getchar();}
while(isdigit(ch)){x=(x<<)+(x<<)+(ch^);ch=getchar();}
return w?-x:x;
}
struct Edge
{
int to;
int val;
int next;
}edge[];
inline void add(int u,int v,int w)
{
edge[++cnt].to=v;
edge[cnt].val=w;
edge[cnt].next=head[u];
head[u]=cnt;
return;
}
bool bfs()
{
queue<int> q;
while(!q.empty()) q.pop();
for(int i=;i<=N;i++) d[i]=-;
d[S]=;
q.push(S);
while(!q.empty())
{
int u=q.front();
q.pop();
for(int i=head[u];i!=-;i=edge[i].next){
int v=edge[i].to;
if(d[v]==-&&edge[i].val>){
d[v]=d[u]+;
q.push(v);
}
}
}
return d[T]!=-;
}
int dfs(int u,int flow)
{
int nowflow=;
if(u==T) return flow;
for(int i=cur[u];i!=-;i=edge[i].next){
cur[u]=i;
int v=edge[i].to;
if(d[v]==d[u]+&&edge[i].val>){
if(int k=dfs(v,min(flow-nowflow,edge[i].val))){
edge[i].val-=k;
edge[i^].val+=k;
nowflow+=k;
if(nowflow==flow) break;
}
}
}
if(!nowflow) d[u]=-;
return nowflow;
}
int Dinic()
{
int ans=;
while(bfs())
{
for(int i=;i<=N;i++) cur[i]=head[i];
ans+=dfs(S,inf);
}
return ans;
}
int main()
{
//IO;
N=read(),M=read(),S=read(),T=read();
int A,B,C;
cnt=-;
for(int i=;i<=N;i++) head[i]=-;
while(M--)
{
A=read(),B=read(),C=read();
add(A,B,C),add(B,A,);
}
printf("%d\n",Dinic());
}

P3376 网络最大流模板(Dinic + dfs多路增广优化 + 炸点优化 + 当前弧优化)的更多相关文章

  1. 【Luogu】P3376网络最大流模板(Dinic)

    最大流模板成为另一个被攻克的模板题. 今天QDC给我讲了一下Dinic,感觉很好懂.于是为了巩固就把这道题A掉了. 核心思想就是不断BFS分层,然后不断DFS找增广路.找不到之后就可以把答案累加输出了 ...

  2. 【Luogu】P3381最小费用最大流模板(SPFA找增广路)

    题目链接 哈  学会最小费用最大流啦 思路是这样. 首先我们有一个贪心策略.如果我们每次找到单位费用和最短的一条增广路,那么显然我们可以把这条路添加到已有的流量里去——不管这条路的流量是多大,反正它能 ...

  3. LOJ 2979 「THUSCH 2017」换桌——多路增广费用流

    题目:https://loj.ac/problem/2979 原来的思路: 优化连边.一看就是同一个桌子相邻座位之间连边.相邻桌子对应座位之间连边. 每个座位向它所属的桌子连边.然后每个人建一个点,向 ...

  4. P3376 网络流-最大流模板题(Dinic+当前弧优化)

    (点击此处查看原题) Dinic算法 Dinic算法相对于EK算法,主要区别在于Dinic算法对图实现了分层,使得我们可以用一次bfs,一次dfs使得多条增广路得到增广 普通的Dinic算法已经可以处 ...

  5. 网络最大流算法—Dinic算法及优化

    前置知识 网络最大流入门 前言 Dinic在信息学奥赛中是一种最常用的求网络最大流的算法. 它凭借着思路直观,代码难度小,性能优越等优势,深受广大oier青睐 思想 $Dinic$算法属于增广路算法. ...

  6. POJ2584 T-Shirt Gumbo——网络最大流模板

    题目:http://poj.org/problem?id=2584 像模板一样的简单题.继续使用 & 的当前弧优化和神奇的构造函数. #include<iostream> #inc ...

  7. bzoj 5120 无限之环 & 洛谷 P4003 —— 费用流(多路增广SPFA)

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=5120 https://www.luogu.org/problemnew/show/P4003 ...

  8. POJ 1459 Power Network(网络最大流,dinic算法模板题)

    题意:给出n,np,nc,m,n为节点数,np为发电站数,nc为用电厂数,m为边的个数.      接下来给出m个数据(u,v)z,表示w(u,v)允许传输的最大电力为z:np个数据(u)z,表示发电 ...

  9. 洛谷P3376 (最大流模板)

    1 #include<bits/stdc++.h> 2 #define int long long 3 using namespace std; 4 const int maxn=5005 ...

随机推荐

  1. Python语法速查:目录

    1. 数据类型与内置函数 2. 列表.元组.字典.集合操作 3. 字符串格式化 4. 字符串常用操作 5. 运算符.math模块.表达式 6. 循环与迭代 7. 函数基础 8. 类与对象 9. 函数进 ...

  2. 005.SQLServer AlwaysOn可用性组高可用简介

    一 AlwaysOn 可用性组 1.1 AlwaysOn 可用性组概述 AlwaysOn 可用性组功能是一个提供替代数据库镜像的企业级方案的高可用性和灾难恢复解决方案.SQL Server 2012 ...

  3. java.lang.IllegalStateException: getOutputStream() has already been called 解决办法

    因为在使用的时候没有使用@ResponseBody这个注解,所以才会报上面的异常

  4. JS时间戳与时间字符串之间的相互转换

    时间字符串 转 时间戳 /** * 时间字符串 转 时间戳 * @param {String} time_str 时间字符串(格式"2014-07-10 10:21:12") * ...

  5. 第04组 Beta冲刺(3/4)

    队名:斗地组 组长博客:地址 作业博客:Beta冲刺(3/4) 各组员情况 林涛(组长) 过去两天完成了哪些任务: 1.分配展示任务 2.收集各个组员的进度 3.写博客 展示GitHub当日代码/文档 ...

  6. 2019年跨越速递Java工程师笔试题

    1.下面哪个选项可以用于JSP页面之间传递对象(A C) A application B page C session D error  E response 评语:这道题考察的是对JSP内置对象的了 ...

  7. 使用VBA从工作表中读图片,以及给工作表中写文件

    因为工作的原因,需要用到VBA,碰到读图片和写图片: Sub Macro01() '从工作表中保存图片 Application.ScreenUpdating = False Dim pth, shp, ...

  8. QAnet Encoder

    #!/usr/bin/python3# -*- coding: utf-8 -*-'''date: 2019/8/19mail: cally.maxiong@gmail.comblog: http:/ ...

  9. [灵魂拷问]MySQL面试高频100问(工程师方向)

    作者:呼延十 juejin.im/post/5d351303f265da1bd30596f9 前言 本文主要受众为开发人员,所以不涉及到MySQL的服务部署等操作,且内容较多,大家准备好耐心和瓜子矿泉 ...

  10. 一起学Android之Xml与Json解析

    概述 在网络中,数据交互通常是以XML和Json的格式进行,所以对这两种格式的数据进行解析,是Android开发中的必备功能,本文以一个简单的小例子,简述Android开发中Xml和Json解析的常用 ...