为什么叫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_S\leq n-1\)。

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

步骤

  • 初始化,BFS出层次图
  • 每次沿着\(d_v=d_u-1\)的边进行搜索,找到一条増广路就増广
  • 如果没有从\(u\)出发到\(T\)的路径,就更新\(d_u\),往回退一步
  • 当\(d_S\geq 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\n",ans);
return 0;
}

ISAP算法的更多相关文章

  1. ISAP算法对 Dinic算法的改进

    ISAP算法对 Dinic算法的改进: 在刘汝佳图论的开头引言里面,就指出了,算法的本身细节优化,是比较复杂的,这些高质量的图论算法是无数优秀算法设计师的智慧结晶. 如果一时半会理解不清楚,也是正常的 ...

  2. 网络流-最大流问题 ISAP 算法解释(转自Renfei Song's Blog)

    网络流-最大流问题 ISAP 算法解释 August 7, 2013 / 编程指南 ISAP 是图论求最大流的算法之一,它很好的平衡了运行时间和程序复杂度之间的关系,因此非常常用. 约定 我们使用邻接 ...

  3. ISAP 算法的学习

    http://www.renfei.org/blog/isap.html 算法与数学 网络流-最大流问题 ISAP 算法解释 2013-08-07Renfei Song 2 条评论 内容提要 [隐藏] ...

  4. 网络流 ISAP算法

    网络流问题: 我自己理解,在流网络中,在不违背容量限制的条件下,解决各种从源点到汇点的问题. ISAP算法概念: 据说不会有卡ISAP时间的题目---时间复杂度O(E^2*V) 首先原理都是基于不断寻 ...

  5. P3376 【模板】网络最大流( Edmonds-krap、Dinic、ISAP 算法)

    P3376 [模板]网络最大流( Edmonds-krap.Dinic.ISAP 算法) 题目描述 如题,给出一个网络图,以及其源点和汇点,求出其网络最大流. 输入格式 第一行包含四个正整数N.M.S ...

  6. HDOJ-3416(最大流+最短路+ISAP算法+向前星dijikstra算法+如何判断一条边是否在最短路中)

    Marriage Match IV HDOJ-3416 这题的题意就是要找两点之间最短路的路径个数,而且边不能重复. 最大流和最短路的结合.首先正向和反向建图,再跑两遍dijikstra.到这里就求出 ...

  7. ISAP 算法

    Dinic 算法其实已经足够处理大多数的网络流了,但还不够快.接下来介绍的是最优秀的增广路最大流算法:ISAP(Improve Shortest Argumenting Path).它的时间复杂度上界 ...

  8. CCF(引水入城:60分):最大流+ISAP算法

    引水入城 201703-5 这从题目分析来看很像最大流的问题,只需要增加一个超级源点和一个超级汇点就可以按照题意连边再跑最大流算法. 因为数据量太大了,肯定会超时.但是没有想到可行的解决方法. #in ...

  9. HDU4280 Island Transport —— 最大流 ISAP算法

    题目链接:https://vjudge.net/problem/HDU-4280 Island Transport Time Limit: 20000/10000 MS (Java/Others)   ...

随机推荐

  1. 旋转数组的最小数字 - 剑指offer 面试题8

    题目描述: 把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转.输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素.例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋 ...

  2. 关于iframe页面里的重定向问题

    最近公司做的一个功能,使用了iframe,父页面内嵌子页面,里面的坑还挺多的,上次其实就遇到过,只不过今天在此描述一下. 请允许我画个草图: 外层大圈是父级页面,里层是子级页面,我们是在父级引用子级页 ...

  3. Python_socket常见的方法、网络编程的安全注意事项、socketsever模块、浏览器中在一段时间记录用户的登录验证机制

    1.socket常见的方法 socket_常见方法_服务器端 import socket from socket import SOL_SOCKET,SO_REUSEADDR sk = socket. ...

  4. 软件工程(FZU2015) 赛季得分榜,第二回合

    SE_FZU目录:1 2 3 4 5 6 7 8 9 10 11 12 13 积分规则 积分制: 作业为10分制,练习为3分制:alpha30分: 团队项目分=团队得分+个人贡献分 个人贡献分: 个人 ...

  5. Shell脚本1

    1Shell编程 Shell 是一个用 C 语言编写的程序,它是用户使用 Linux 的桥梁.Shell 既是一种命令语言,又是一种程序设计语言. Shell脚本 Shell 脚本(shell scr ...

  6. nginx学习笔记二

    一,nginx架构在Linux系统中以daemon(守护进程)的方式在后台运行,后台进程包含一个master进程和多个worker进程(多进程的工作方式) master进程 | 信号 | | ---- ...

  7. MySQL慢查询日志配置方式 slow_query_log

    MySQL慢查询(一) - 开启慢查询 - 鲁玉成 - 博客园 https://www.cnblogs.com/luyucheng/p/6265594.html mysql开启慢查询方法 - lava ...

  8. 图片转字符画 【学习ing】

    1.创建ascii.py 2. 下面是 ascii.py 的完整代码: from PIL import Image import argparse #命令行输入参数处理 parser = argpar ...

  9. [转帖]HTTP 头部解释

    HTTP 头部解释 https://www.cnblogs.com/poissonnotes/p/4844014.html 之前看的太粗了 同事闻起来 referer 才知道自己所知甚少.. ==== ...

  10. childNodes遍历DOM节点树

    childNodes遍历DOM节点树 var s = ""; function travel(space,node) { if(node.tagName){ s += space ...