网络流小结

这两天复习了下网络流,高中学的ISAP忘得七七八八,干脆把Dinic,ISAP和预流推进都重新看了一遍,写了个简洁的小结(基本上就是给自己看的)

网络流

剩余图

顶点的层次:源点到点的最短路径长度

层次图建立在剩余图基础上

阻塞流:不存在增广路时

FF

复杂度O(n*m*u)

容量网络、流量网络、残量网络

用最大流最小割定理证明

EK

复杂度O(n*m*m)

建反向边,每次bfs找最短增广路

最多增广n*m次(每阶段最多增广m次)

Dinic

多路增广

1.初始化容量网络和网络流

2.构造残留网络和层次网络,汇点不在则退出

3.一次DFS以增广

4.转2

比起EK单阶段增广从m*m优化为n*m

最高标号预流推进(HLPP)

预流推进:

定义盈余、活跃节点、h函数、可推流边(h(u)=h(v)+1)

1.令flow(s,v)=cap(s,v),flow(u,v)=0,构造h函数,h(s)=n

2.不存在活顶点则exit

3.选取活顶点v,选可推流边,没有了则令h(v)=min{h(w)+1},再加入队列

FIFO队列n*n*m

先进先出则变为最高标号预流推进,n*n*sqrt(m)

流量一次性从源中出来,推入汇或最终退回源

ISAP

Improved Shortest Augmenting Path

在EK算法基础上,增广后继续增广,直到遇到死路,执行retreat操作(修改d值,重建层次网络)

retreat时d[u]=min{d[v]+1}(u,v是残量网络邻接点,v靠t更近)(保证了最短路),若无邻接点,取d[u]大于等于n

实现:

1.初始化d数组,从t到s逆向进行

2.维护当前节点u

3.不连通时d[s]>=n

4.GAP 优化(重要):retreat时若u是唯一一个和t距离d[u]的点,则exit

5.另一优化:记录上次处理到的邻接边

拿poj1273写了Dinic和ISAP的模板(基本上去掉main就是模板内容)。虽然A了,但可能还有错误,以后做网络流的有发现时候再更正,有空再写个预流推进。

当前ISAP的模板是递归的,以后再多写几个版本出来。

Dinic

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define rep(i,a,b) for (int i=a;i<=b;++i)
#define tra(k,u) for (int k=head[u];k!=-1;k=ed[k].next)

const int MAXN=2000;
const int MAXM=2000;
const int INF=1e9;

struct edge{
    int to,f,next;
}ed[2*MAXM+10];
int head[MAXN],tot;
int d[MAXN];
int q[MAXN];

int n,m,s,e,c,ans;

void init(int point_num)
{
    tot=-1;
    memset(head,-1,sizeof(int)*(point_num+1));
}

void add_edge(int u,int v,int f)
{
    ed[++tot].to=v;ed[tot].f=f;ed[tot].next=head[u];head[u]=tot;
    ed[++tot].to=u;ed[tot].f=0;ed[tot].next=head[v];head[v]=tot;
}

void bfs(int src)
{
    int l=0,r=0,now;
    q[++r]=src;d[src]=0;
    while (l<r)
    {
        now=q[++l];
        tra(k,now) if (ed[k].f && d[ed[k].to]==-1)
        {
            d[ed[k].to]=d[now]+1;
            q[++r]=ed[k].to;
        }
    }
}

int dfs(int now,int delta,int t)
{

    if (now==t) return delta;
    int tempflow=0,stream=0;
    tra(k,now) if (ed[k].f && d[ed[k].to]==d[now]+1)
    {
        stream=dfs(ed[k].to,min(delta,ed[k].f),t);
        tempflow+=stream;
        ed[k].f-=stream;
        ed[k^1].f+=stream;
        delta-=stream;
        if (delta==0) break;
    }
    return tempflow;
}

int maxflow_Dinic(int s,int t,int point_num)
{
    int maxflow=0;
    while (1)
    {
        memset(d,-1,sizeof(int)*(point_num+1));
        bfs(s);
        if (d[t]==-1) return maxflow;
        maxflow+=dfs(s,INF,t);
    }
}

int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        init(m);
        rep(i,1,n)
        {
            scanf("%d%d%d",&s,&e,&c);
            add_edge(s,e,c);
        }
        ans=maxflow_Dinic(1,m,m);
        printf("%d\n",ans);
    }
}

递归ISAP

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define rep(i,a,b) for (int i=a;i<=b;++i)
#define tra(k,u) for (int k=head[u];k!=-1;k=ed[k].next)

const int MAXN=2000;
const int MAXM=2000;
const int INF=1e9;

struct edge{
    int to,f,next;
}ed[2*MAXM+10];
int head[MAXN],tot;
int d[MAXN];
int q[MAXN];
int pre[MAXN]; //增广路上的上一个点
int gap[MAXN]; //gap优化

int n,m,s,e,c,ans;

void init(int point_num)
{
    tot=-1;
    memset(head,-1,sizeof(int)*(point_num+1));
}

void add_edge(int u,int v,int f)
{
    ed[++tot].to=v;ed[tot].f=f;ed[tot].next=head[u];head[u]=tot;
    ed[++tot].to=u;ed[tot].f=0;ed[tot].next=head[v];head[v]=tot;
}

void bfs(int t)
{
    int l=0,r=0,now;
    q[++r]=t;d[t]=0;
    while (l<r)
    {
        now=q[++l];
        tra(k,now) if (ed[k^1].f && d[ed[k].to]==-1)
        {
            d[ed[k].to]=d[now]+1;
            q[++r]=ed[k].to;
        }
    }
}

int dfs(int now,int delta,int t,int point_num)
{
    if (now==t) return delta;
    int stream=0,tempflow=0,mind=point_num;
    tra(k,now)
    {
        if (ed[k].f)
        {
            if (d[ed[k].to]+1==d[now])
            {
                stream=dfs(ed[k].to,min(delta,ed[k].f),t,point_num);
                tempflow+=stream;
                ed[k].f-=stream;
                ed[k^1].f+=stream;
                delta-=stream;
                if (d[s]>=point_num) return tempflow;
                if (delta==0) break;
            }
            mind=min(mind,d[ed[k].to]+1);
        }
    }

    if (tempflow==0)
    {
        --gap[d[now]];
        if (gap[d[now]]==0) d[s]=point_num;
        d[now]=mind;
        ++gap[d[now]];
    }

    return tempflow;
}

int maxflow_ISAP(int s,int t,int point_num)
{
    int u=0,ans=0;
    memset(d,-1,sizeof(int)*(point_num+1));
    memset(gap,0,sizeof(int)*(point_num+1));
    bfs(t);                             //bfs可去掉,效率稍微降低
    rep(i,1,n) ++gap[d[i]];
    while (d[s]<point_num)
    {
        ans+=dfs(s,INF,t,point_num);
    }
    return ans;
}

int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        init(m);
        rep(i,1,n)
        {
            scanf("%d%d%d",&s,&e,&c);
            add_edge(s,e,c);
        }
        ans=maxflow_ISAP(1,m,m);
        printf("%d\n",ans);
    }
}
时间: 2024-10-12 13:12:37

网络流小结的相关文章

上下界网络流小结

虽然网上已经有很详细的解释了,但是别人总结的终究是别人的. 1无源无汇上下界的可行流 首先明确定义:B[u, v]和C[u, v]表示边(u,v)的下界容量和上界容量,我们的问题是求一个每条边都具有上下界的可行流. 简单分析:由于每条边要满足下界容量,而且每个点要满足流量平衡,所以就需要对平时的网络流模型改造. 方法一:建立附加远点回电,S,T,对于边(u, v),连边S->v,容量B[u, v], 及u->T, 容量B[u, v], 然后对于本来的边u->v,容量改为C[u, v]-B

网络流小结+[jzyzoj p1320] patrol

一个不能更清楚的网络流介绍 ↑虽然不是我写的但是观摩一下总是没问题的嗯 看到晗神学的是神奇的ek算法. 但是看起来还是Ford-Fulkerson比较简单..所以我就学了这个...嗯其他的先看看..这个似乎比较好上手.... 从题目要求来看,我们只需要建一个双向图,然后用神奇的网络流算法算出到终点的最大流量-1即可 ( 因为留一条路出来就可以满足来回不能只经过每个土地1次或者0次 ) 但是并不对 如上为本题某数据 ( 好像是in3 ) 的建图[我知道我画的很丑],所示从1到7的流为2,但是放狗数

(转)一句话小结各种网络流)

最大流:DINIC or SAP 最小费用最大流:SPFA+增广(费用的值较离散) or ZKW(费用的值集中) 有源汇的上下界最大流:新建s', t',用(i, j, l, r)表示i到j有一条下界为l上界为r的边,将每条这样的边拆成(s', j, 0, l), (i, t', 0, l), (i, j, 0, r-l),加入边(t, s, 0, max)再从s‘到t'求最大流,再去掉(t, s, 0, max)这条边,从s到t求最大流 有源汇的上下界最小可行流:基本同上,将最后一步改成从t到

小结:网络流

概要: 这货很强大啊.isap和dinic都算很快的算法,目前貌似卡不了?spfa在费用流中找增广路.上下界的网络流可以用分离必要弧来做. 应用: 解决许多多约束最优化的问题. 技巧及注意: 网络流在于建模,但是首先得有个基础. 上下界网络流:整体思想就是分离下界,将原边连成上界-下界,终点的界和+=这个下界,起点的界和-=这个下界.处理完后,然后扫这些点,如果界和大于0,附加源连这个点容量为界和,反之连到附加汇容量为界和绝对值.具体看有上下界的网络流问题.

网络流24题小结

网络流24题 前言 网络流的实战应用篇太难做了,因此先完善这一部分 ## 第一题:飞行员配对方案 \(BSOJ2542\)--二分图 最优匹配 题意 两国飞行员\(x\)集合\(y\)集合,\(x\)飞行员可以配对特定的\(y\)集合的飞行员(可无),求一对一配对最大数 Solution 二分图最大匹配裸题,最大流实现 建图:(设\(i\in x\)而\(i'\in y\)) \((S,i,1)~(i',T,1)\) 对\((i,j')\)可匹配\((i,j',1)\) Code 略 ## 第二

图论专题小结:网络流算法之ISAP算法

ISAP算法 ISAP(Improved Shortest Augument Path)算法是改进版的SAP算法,如果对效率要求很高的时候,可以用该算法. (1)概述:算法基于这样的一个事实:每次增广之后,任意结点到汇点(在残余网络中)的最短距离都不会减小.这样,我们可以利用d[i[表示结点i到汇点的距离的下界.然后再增广过程当中不断地修改这个下界.增广的时候和Dinic算法类似,只允许沿着d[i]==d[j]+1的弧(i,j)走. 不难证明,d[i[满足两个条件:(1)d[t]=0;(2)对任

欧拉路,欧拉回路小结(转)

欧拉路,欧拉回路小结 把欧拉路和欧拉回路做一个小总结,包含了一些题目,以后遇到新的我还会陆续加上. 定义: 给定无孤立结点图G,若存在一条路,经过G中每条边有且仅有一次,称这条路为欧拉路,如果存在 一条回路经过G每条边有且仅有一次,称这条回路为欧拉回路.具有欧拉回路的图成为欧拉图. 关于欧拉路和欧拉回路定义及存在证明请看这里. 这里给出欧拉路和欧拉回路存在条件的结论: 存在欧拉路的条件: 无向图:  图连通,所有点都是偶数度,或者只有两个点是奇数度.当所有点是偶数度时欧拉路起点可以是任意 点:当

[bzoj1565][NOI2009]植物大战僵尸_网络流_拓扑排序

植物大战僵尸 bzoj1565 题目大意:给你一张网格图,上面种着一些植物.你从网格的最右侧开始进攻.每个植物可以对僵尸提供能量或者消耗僵尸的能量.每个植物可以保护一个特定网格内的植物,如果一个植物被保护,那么如果僵尸想吃掉该植物就必须先吃掉保护它的植物.问:僵尸最多能获得多少能量. 注释:1<=N(网格的宽)<=20,1<=M(网格的长)<=30,-20,000<=代价和收益<=20,000. 想法:前置题目([NOI2006]最大获利).这道题和最大获利比较相像,如

使用Apache POI导出Excel小结--导出XLS格式文档

使用Apache POI导出Excel小结 关于使用Apache POI导出Excel我大概会分三篇文章去写 使用Apache POI导出Excel小结--导出XLS格式文档 使用Apache POI导出Excel小结--导出XLSX格式文档 使用Apache POI导出Excel--大数量导出 导出XLS格式文档 做企业应用项目难免会有数据导出到Excel的需求,最近在使用其,并对导出Excel封装成工具类开放出来供大家参考.关于Apache POI Excel基本的概念与操作我在这里就不啰嗦